二叉樹的層序遍歷和二叉樹的線索化


先根,后子樹;先左子樹,后右子樹
二叉樹的根節點 a 入隊
a 的子樹,根節點 b 和 c 分別入隊
然后 b 的子樹的根節點入隊(為空)
c 的子樹的根節點入隊
d 的子樹的根節點入隊(為空)
e 的子樹的根節點入隊
f 的子樹的根節點入隊(為空)
g的子樹的根節點入隊(為空)結束層序遍歷,整個過程就是一層層的遍歷,依靠一個隊列來存放臨時查找的結點。
 
二叉樹線索化

問題的提出:當以二叉鏈表作為存儲結構時,只能找到結點的左右孩子的信息,而不能直接找到結點的任一序列的前驅與后繼信息,這種信息只有在遍歷的動態過程中才能得到,為了能保存所需的信息,可增加標志域;

其中:  ltag=0,lchild 域指示結點的左孩子,ltag= 1,lchild 域指示結點的前驅,rtag=0,rchild 域指示結點的右孩子,rtag=1 ,rchild 域指示結點的后驅。

以這種結構構成的二叉鏈表作為二叉樹的存儲結構,叫做線索鏈表。其中指向結點前驅與后繼的指針叫做線索,加上線索的二叉樹稱之為線索二叉樹。

對二叉樹以某種次序遍歷使其變為線索二叉樹的過程叫線索化

模仿線性表的存儲結構,在二叉樹的線索鏈表上也添加一個頭結點,令其lchild域的指針指向二叉樹的根結點,其rchild域的指針指向中序遍歷時訪問的最后一個結點,同時,令二叉樹中序序列中的第一個結點lchild域的指針和最后一個結點rchild域的指針均指向頭結點;就像為二叉樹建立了一個雙向線索鏈表,就好比人在一個圓圈上走路,有可能存在好走的可能性。

中序遍歷序列為:D B E A H F C G

中序線索二叉樹, ltag=0,lchild 域指示結點的左孩子,ltag= 1,lchild 域指示結點的前驅,rtag=0,rchild 域指示結點的右孩子,rtag=1 ,rchild 域指示結點的后驅,二叉樹的二叉線索存儲表示:

//二叉樹的二叉線索存儲表示:
typedef enum{
    Link, Thread
} PointerTag;
//Link == 0:指針,  Thread == 1:線索

typedef struct BiThrNode{
    int data;
    BiThrNode *lchild, *rchild;//左右孩子指針域
    PointerTag  LTag, Rtag;//左右指針或者線索的標記
} BiTreeNode, *BiThrTree;

中序遍歷序列為:D B E A H F C G

先序線索二叉樹

先序序列:  A B C D E F G H K

后續線索二叉樹

后序序列:  D C B H K G F E A

如何遍歷線索二叉樹(中序遍歷為例)

結點的后繼:若是葉子結點,其右標志是“1”,即右鏈是線索,指示其后繼;否則遍歷其右子樹時訪問的第一個結點,即右子樹中最左下的結點。

結點的前趨:若其左標志為“1”,則左鏈為線索,指示其前驅,否則遍歷左子樹時的最后訪問的一個結點(左子樹最右下的結點)為其前驅。

什么時候采用線索鏈表做存儲結構?

程序中所用二叉樹需要經常遍歷或查找結點在遍歷所得線性序列中的前驅和后繼時。

線索鏈表的遍歷算法(中序找后繼法)

 

模仿線性表的存儲結構,在二叉樹的線索鏈表上也添加一個頭結點,令其lchild域的指針指向二叉樹的根結點,其rchild域的指針指向中序遍歷時訪問的最后一個結點,同時,令二叉樹中序序列中的第一個結點lchild域的指針和最后一個結點rchild域的指針均指向頭結點;就像為二叉樹建立了一個雙向線索鏈表 

 1 //二叉樹的二叉線索存儲表示:
 2 typedef enum{
 3     Link, Thread
 4 } PointerTag;
 5 //Link == 0:指針,  Thread == 1:線索
 6 
 7 //線索二叉樹結點結構表示
 8 typedef struct BiThrNode{
 9     int data;
10     BiThrNode *lchild, *rchild;//左右孩子指針域
11     PointerTag  LTag, RTag;//左右指針或者線索的標記
12 } BiTreeNode, *BiThrTree;
13 
14 //訪問結點
15 bool visit(int data)
16 {
17     //訪問函數
18     return true;
19 }
20 
21 //中序找后繼法,線索二叉樹的遍歷
22 bool InOrderTraverse_Thr(BiThrTree T)
23 {
24     //二叉線索樹的中序遍歷,T 是頭結點,令其lchild域的指針指向二叉樹的根結點
25     BiTreeNode *p = T->lchild;
26     //當遍歷完畢的結束條件
27     while (p != T)
28     {
29         //p 結點的左標記為指針(ltag=0)。則左指針指向左孩子
30         while (p->LTag == Link)
31         {
32             p = p->lchild;
33         }
34         //中序遍歷
35         //訪問
36         visit(p->data);
37         //左邊沒有了,訪問右邊,p 結點的右標記是線索, rtag == 1.且其右孩子指針沒有指向根節點的時候
38         while (p->RTag == Thread && p->rchild != T)
39         {
40             //訪問的是p 的后繼(也就是根節點)
41             p = p->rchild;
42             //訪問
43             visit(p -> data);
44         }
45         //最后找右子樹
46         p = p->rchild;
47     }
48         
49     return true;
50 }
如何對二叉樹進行線索化?
建立線索二叉樹的過程,實質上就是在遍歷的過程中,檢查當前結點的左右指針是否為空,如果為空,將它們改為指向其前驅或后繼的線索。
設指針pre始終指向剛剛訪問過的結點,指針p指向當前正在訪問的結點。即pre為p的前驅結點,p為pre的后繼結點,則在線索化的過程中,訪問結點p所做的處理如下(以中序線索化為例)

(1)建立p的前驅線索。若p->lchild為空,則將其左標志置1,並令p->lchild指向其前驅pre

(2)建立pre的后繼線索。若pre->rchild為空,則將其右標志置1,並令pre->rchild指向其后繼p

(3)將pre指向p剛剛訪問過的結點,即pre=p

 

//二叉樹的二叉線索存儲表示:
typedef enum{
    Link, Thread
} PointerTag;
//Link == 0:指針,  Thread == 1:線索

//線索二叉樹結點結構表示
typedef struct BiThrNode{
    int data;
    BiThrNode *lchild, *rchild;//左右孩子指針域
    PointerTag  LTag, RTag;//左右指針或者線索的標記
} BiTreeNode, *BiThrTree;

//訪問結點
bool visit(int data)
{
    //訪問函數
    return true;
}

//對已經存在的二叉樹進行中序線索化(遞歸方法)
void InThreading(BiThrTree p)
{
    //指向上一個剛剛訪問過的結點
    BiTreeNode *pre;
    if (p)
    {
        // 左子樹線索化
        InThreading(p->lchild);
        //如果左子樹為空,則加線索
        if (!p->lchild)
        {
            //加線索
            p->LTag = Thread;
            //指向前驅結點
            p->lchild = pre;
        }
        //如果右子樹為空
        if (!pre->rchild)
        {
            //加線索
            pre->RTag = Thread;
            //指向后繼結點
            pre->rchild = p;
        }
        // 保持 pre 指向 p 的前驅
        pre = p;
        // 右子樹線索化
        InThreading(p->rchild);
    }
}

//新建一個線索二叉樹
bool InOrderThreading(BiThrTree Thrt, BiThrTree T)
{
    //指向上一個剛剛訪問過的結點
    BiTreeNode *pre;
    // 新建頭結點
    if (!(Thrt = (BiTreeNode*)malloc(sizeof(BiThrNode))))
    {
        exit(1);
    }
    //頭結點新建成功
    Thrt->LTag = Link;
    Thrt->RTag = Thread;
    //右指針回指
    Thrt->rchild = Thrt;
    // 若二叉樹空,則左指針回指
    if (!T)
    {
        Thrt->lchild = Thrt;
    }
    else
    {
        Thrt->lchild = T;
        pre = Thrt;
        // 中序遍歷進行中序線索化
        InThreading(T);
        pre->rchild = Thrt;
        pre->RTag = Thread;
        // 最后一個結點線索化
        Thrt->rchild = pre;
    }
    
    return true;
}

 

歡迎關注

 

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM