1. 程式人生 > 其它 >線索二叉樹

線索二叉樹

技術標籤:演算法JAVA演算法資料結構二叉樹指標資料結構java

一,簡介
首先,二叉樹是非線性資料結構,而遍歷序列是線形序列,二叉樹遍歷實際上是將一個非線性結構進行線性化的操作。根據線性序列的特性,除了第一個元素外,每一個節點都有唯一的前驅,除了最後一個元素外,每一個節點都有唯一的後繼。採用二叉連結串列儲存時,只記錄了左右孩子的資訊無法直接得到每個結點的前驅和後繼。
二,儲存結構
1,二叉樹採用二叉連結串列儲存時。每個結點有兩個指標域。如果二叉連結串列有n個結點,則一共有2n個指標域。而只有n-1個是實指標,其餘n+1個是空指標。
原因
因為二叉樹有n-1個分支,而每個分支對應一個實指標,如圖所示。在這裡插入圖片描述


從下往上看,每個節點對應一個分支,而只有樹根沒有對應分支,因此分支數等於節點數減去一。
而每個分支對應一個實指標,所以有n-1個實指標。總的指標減去實指標數即為空指標數,也就是2n-n+1=n+1。

所以我們可以充分利用空指標記錄節點的前驅和後繼,從而加快查詢節點前驅和後繼的速度。
2,每個節點還是有兩個指標域。
如果結點有左孩子。則lchild指向左孩子,否剛指向其前驅。
如果結點有右孩子,則rchild指向右孩子,否則rchild指向其後繼。

那麼如何區分儲存的是左孩子還是右孩子,還是前驅和後繼呢?為了避免混淆,增加兩個標誌域ltag,rtag。

在這裡插入圖片描述
在這裡插入圖片描述
程式碼如下

public static
class BNode{ String data; BNode lchild,rchild; int rtag,ltag;//右標誌,左標誌 }

三**,構造線索二叉樹**
1,線索二叉樹分為前序線索二叉樹,中序線索二叉樹和後序線索二叉樹。
2,二叉樹線索化的過程實際上是在遍歷過程中修改空指標的過程。利用儲存結構的空指標指向前驅或者後繼。
所以我們可以設定兩個指標,一個指標pre指向剛剛訪問的節點,另一個指標p指向當前節點。
也就是說pre指向的結點為p指向的結點的前驅,反之,p指向的結點為pre指向的結點的後繼。
3,在遍歷的過程中,如果當前節點p的左孩子為空,則該節點的lchild指向其前驅.即p.lchild=pre;

如果pre節點的右孩子為空,則該節點的rchild指向其後繼,即pre.rchild=p;
4*,演算法步驟:*
①指標p指向根節點,pre初始化為空,pre永遠指向p的前驅。
②若p非空,則
a,中序遍歷線索化p的左子樹
b,若p的左子樹為空,則給p加上左線索,即p.ltag=1。p
的左子樹指標指向pre(前驅),即p.lchild=pre。 否則令p.ltag=0;
c,如果pre非空,則判斷如果pre的右子樹為空,給pre加上右線索,即pre.rtag=1。Pre的右孩子指標指向p.即 pre.rchild=p,否則pre.rtag=0。
d,p賦值給pre,轉向pre的右子樹。
e,中序遍歷線索化p的右子樹。
③處理最後一個節點,令其後繼為空,即pre.rchiid=null,pre.rtag=1;
程式碼如下

 public static void inthread(BNode p){
        //中序線索化
        if(p!=null){
            inthread(p.lchild);//不為空則一直找到其左子(子序遍歷的左)
            if(p.lchild==null){//中序遍歷的中
                p.ltag=1;
                p.lchild=pre;
            }
            else{
                p.ltag=0;
            }
            if(pre!=null){//增加右線索
                if(pre.rchild==null){//增加了
                    pre.rtag=1;
                    pre.rchild=p;
                }
                else{
                    pre.rtag=0;
                }
            }
            pre=p;
            inthread(p.rchild);//中序遍歷的右
        }
    }

四,線索遍歷二叉樹

1,指標p指向根結點。
2,若p非空,則重複以下操作。
(中序遍歷,左中右)
P指標沿左孩子向下,找到最左結點,他是中序遍歷的第一個結點。
訪問p節點。
沿著右線索查詢當前節點p的後繼節點並訪問,直到右線索為零或遍歷結束。
3,遍歷p的右子樹。
程式碼如下

 public static void inorderthread(BNode t){
        //根據中序,線索化進行遍歷
        BNode p;
        p=t;
        while (p!=null){
            //左
            while (p.ltag==0){
                p=p.lchild;
            }
            //中
            System.out.print(p.data+" ");
            //右
            while (p.rtag==1&&p.rchild!=null){
                //右線索存在
                p=p.rchild;
                System.out.print(p.data+" ");
            }
            p=p.rchild;//不是線索,轉向右節點
        }
    }

最後附上完整程式碼

package Tree;

import java.util.Scanner;

public class xiansuotree {
    public static class BNode{
        String data;
        BNode lchild,rchild;
        int rtag,ltag;//右標誌,左標誌
    }

    static  BNode pre;

    public static BNode creattree(BNode t){
        String ch;
        Scanner scanner=new Scanner(System.in);
        ch=scanner.nextLine();//判斷是否有#

        if(ch.equals("#")){
            t=null;
        }
        else{
            t=new BNode();
            t.data=ch;//先序遍歷生成樹
            t.lchild=creattree(t.lchild);
            t.rchild=creattree(t.rchild);
        }
        return t;
    }

    static void inorder(BNode t){//中序遍歷
        if(t!=null){
            inorder(t.lchild);
            System.out.print(t.data+" ");
            inorder(t.rchild);
        }
    }

    public static void inthread(BNode p){
        //中序線索化
        if(p!=null){
            inthread(p.lchild);//不為空則一直找到其左子(子序遍歷的左)
            if(p.lchild==null){//中序遍歷的中
                p.ltag=1;
                p.lchild=pre;
            }
            else{
                p.ltag=0;
            }
            if(pre!=null){//增加右線索
                if(pre.rchild==null){//增加了
                    pre.rtag=1;
                    pre.rchild=p;
                }
                else{
                    pre.rtag=0;
                }
            }
            pre=p;
            inthread(p.rchild);//中序遍歷的右
        }
    }

    public static void creatinthread(BNode t){
        pre=new BNode();
        if(t!=null){
            inthread(t);
            //最後一個節點,其後繼為空
            pre.rchild=null;
            pre.rtag=1;
        }
    }

    public static void inorderthread(BNode t){
        //根據中序,線索化進行遍歷
        BNode p;
        p=t;
        while (p!=null){
            //左
            while (p.ltag==0){
                p=p.lchild;
            }
            //中
            System.out.print(p.data+" ");
            //右
            while (p.rtag==1&&p.rchild!=null){
                //右線索存在
                p=p.rchild;
                System.out.print(p.data+" ");
            }
            p=p.rchild;//不是線索,轉向右節點
        }
    }

    public static void main(String[] args) {
        System.out.println("按先序輸入二叉樹中節點的值(孩子為空時輸入#)");
        BNode node=new BNode();
        node=creattree(node);

        //ABD##E##CF#G###
        System.out.println("中序");
        inorder(node);
        System.out.println();
        creatinthread(node);
        inorderthread(node);


    }
}