完全理解線索二叉樹

1、線索二叉樹的原理html

    經過考察各類二叉鏈表,無論兒叉樹的形態如何,空鏈域的個數老是多過非空鏈域的個數。準確的說,n各結點的二叉鏈表共有2n個鏈域,非空鏈域爲n-1個,但其中的空鏈域卻有n+1個。以下圖所示。web


    所以,提出了一種方法,利用原來的空鏈域存放指針,指向樹中其餘結點。這種指針稱爲線索。svg

    記ptr指向二叉鏈表中的一個結點,如下是創建線索的規則:函數

    (1)若是ptr->lchild爲空,則存放指向中序遍歷序列中該結點的前驅結點。這個結點稱爲ptr的中序前驅;ui

    (2)若是ptr->rchild爲空,則存放指向中序遍歷序列中該結點的後繼結點。這個結點稱爲ptr的中序後繼;spa

    顯然,在決定lchild是指向左孩子仍是前驅,rchild是指向右孩子仍是後繼,須要一個區分標誌的。所以,咱們在每一個結點再增設兩個標誌域ltag和rtag,注意ltag和rtag只是區分0或1數字的布爾型變量,其佔用內存空間要小於像lchild和rchild的指針變量。結點結構以下所示。.net


    其中:
3d

    (1)ltag爲0時指向該結點的左孩子,爲1時指向該結點的前驅;指針

    (2)rtag爲0時指向該結點的右孩子,爲1時指向該結點的後繼;code

    (3)所以對於上圖的二叉鏈表圖能夠修改成下圖的養子。



2、線索二叉樹結構實現

    二叉線索樹存儲結構定義以下:

 
 
 
 
  1. /* 二叉樹的二叉線索存儲結構定義*/
  2. typedef enum{Link, Thread}PointerTag; //Link = 0表示指向左右孩子指針;Thread = 1表示指向前驅或後繼的線索
  3. typedef struct BitNode
  4. {
  5. char data; //結點數據
  6. struct BitNode *lchild, *rchild; //左右孩子指針
  7. PointerTag Ltag; //左右標誌
  8. PointerTag rtal;
  9. }BitNode, *BiTree;

  線索化的實質就是將二叉鏈表中的空指針改成指向前驅或後繼的線索。因爲前驅和後繼信息只有在遍歷該二叉樹時才能獲得,因此,線索化的過程就是在遍歷的過程當中修改空指針的過程。

    中序遍歷線索化的遞歸函數代碼以下:

 
 
 
 
  1. BiTree pre; //全局變量,始終指向剛剛訪問過的結點
  2. //中序遍歷進行中序線索化
  3. void InThreading(BiTree p)
  4. {
  5. if(p)
  6. {
  7. InThreading(p->lchild); //遞歸左子樹線索化
  8. //===
  9. if(!p->lchild) //沒有左孩子
  10. {
  11. p->ltag = Thread; //前驅線索
  12. p->lchild = pre; //左孩子指針指向前驅
  13. }
  14. if(!pre->rchild) //沒有右孩子
  15. {
  16. pre->rtag = Thread; //後繼線索
  17. pre->rchild = p; //前驅右孩子指針指向後繼(當前結點p)
  18. }
  19. pre = p;
  20. //===
  21. InThreading(p->rchild); //遞歸右子樹線索化
  22. }
  23. }

上述代碼除了//===之間的代碼之外,和二叉樹中序遍歷的遞歸代碼機會徹底同樣。只不過將打印結點的功能改爲了線索化的功能。


    中間部分代碼作了這樣的事情:

由於此時p結點的後繼尚未訪問到,所以只能對它的前驅結點pre的右指針rchild作判斷,if(!pre->rchild)表示若是爲空,則p就是pre的後繼,因而pre->rchild = p,而且設置pre->rtag = Thread,完成後繼結點的線索化。如圖:


    if(!p->lchild)表示若是某結點的左指針域爲空,由於其前驅結點剛剛訪問過,賦值了pre,因此能夠將pre賦值給p->lchild,並修改p->ltag = Thread(也就是定義爲1)以完成前驅結點的線索化。



    

    完成前驅和後繼的判斷後,不要忘記當前結點p賦值給pre,以便於下一次使用。

    

    有了線索二叉樹後,對它進行遍歷時,其實就等於操做一個雙向鏈表結構。


    和雙向鏈表結點同樣,在二叉樹鏈表上添加一個頭結點,以下圖所示,並令其lchild域的指針指向二叉樹的根結點(圖中第一步),其rchild域的指針指向中序遍歷訪問時的最後一個結點(圖中第二步)。反之,令二叉樹的中序序列中第一個結點中,lchild域指針和最後一個結點的rchild域指針均指向頭結點(圖中第三和第四步)。這樣的好處是:咱們既能夠從第一個結點起順後繼進行遍歷,也能夠從最後一個結點起順前驅進行遍歷。



    遍歷代碼以下所示。

 
 
 
 
  1. //t指向頭結點,頭結點左鏈lchild指向根結點,頭結點右鏈rchild指向中序遍歷的最後一個結點。
  2. //中序遍歷二叉線索樹表示二叉樹t
  3. int InOrderThraverse_Thr(BiTree t)
  4. {
  5. BiTree p;
  6. p = t->lchild; //p指向根結點
  7. while(p != t) //空樹或遍歷結束時p == t
  8. {
  9. while(p->ltag == Link) //當ltag = 0時循環到中序序列的第一個結點
  10. {
  11. p = p->lchild;
  12. }
  13. printf( "%c ", p->data); //顯示結點數據,能夠更改成其餘對結點的操做
  14. while(p->rtag == Thread && p->rchild != t)
  15. {
  16. p = p->rchild;
  17. printf( "%c ", p->data);
  18. }
  19. p = p->rchild; //p進入其右子樹
  20. }
  21. return OK;
  22. }
說明:


    (1)代碼中,p = t->lchild;意思就是上圖中的第一步,讓p指向根結點開始遍歷;
    (2)while(p != t)其實意思就是循環直到圖中的第四步出現,此時意味着p指向了頭結點,因而與t相等(t是指向頭結點的指針),結束循環,不然一直循環下去進行遍歷操做;
    (3)while(p-ltag == Link)這個循環,就是由A->B->D->H,此時H結點的ltag不是link(就是不等於0),因此結束此循環;
    (4)而後就是打印H;
    (5)while(p->rtag == Thread && p->rchild != t),因爲結點H的rtag = Thread(就是等於1),且不是指向頭結點。所以打印H的後繼D,以後由於D的rtag是Link,所以退出循環;
    (6)p=p->rchild;意味着p指向告終點D的右孩子I;
    (7).....,就這樣不斷的循環遍歷,直到打印出HDIBJEAFCG,結束遍歷操做。


    從這段代碼能夠看出,它等因而一個鏈表的掃描,因此時間複雜度爲O(n)。
    因爲充分利用了空指針域的空間(等於節省了空間),又保證了建立時的一次遍歷就能夠終生受用後繼的信息(意味着節省了時間)。因此在實際問題中,若是所用的二叉樹須要通過遍歷或查找結點時須要某種遍歷序列中的前驅和後繼,那麼採用線索二叉鏈表的存儲結構就是很是不錯的選擇。

 
 
 
 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define ERROR 0
  4. #define OK 1
  5. typedef enum{Link, Thread} PointerTag; //link = 0表示指向左右孩子指針
  6. //Thread = 1表示指向前驅或後繼的線索
  7. typedef struct BitNode
  8. {
  9. char data; //結點數據
  10. struct BitNode *lchild; //左右孩子指針
  11. struct BitNode *rchild;
  12. PointerTag ltag; //左右標誌
  13. PointerTag rtag;
  14. }BitNode, *BiTree;
  15. BiTree pre; //全局變量,始終指向剛剛訪問過的結點
  16. //前序建立二叉樹
  17. void CreateTree(BiTree *t)
  18. {
  19. char ch;
  20. scanf( "%c", &ch);
  21. if(ch == '#')
  22. {
  23. *t = NULL;
  24. }
  25. else
  26. {
  27. (*t) = (BiTree) malloc( sizeof(BitNode));
  28. if((*t) == NULL)
  29. {
  30. return;
  31. }
  32. (*t)->data = ch;
  33. CreateTree(&((*t)->lchild));
  34. CreateTree(&((*t)->rchild));
  35. }
  36. }
  37. //t指向頭結點,頭結點左鏈lchild指向根結點,頭結點右鏈rchild指向中序遍歷的最後一個結點。
  38. //中序遍歷二叉線索樹表示的二叉樹t
  39. int InOrderThraverse_Thr(BiTree t)
  40. {
  41. BiTree p;
  42. p = t->lchild; //p指向根結點
  43. while(p != t)
  44. {
  45. while(p->ltag == Link) //當ltag = 0時循環到中序序列的第一個結點
  46. {
  47. p = p->lchild;
  48. }
  49. printf( "%c ", p->data); //顯示結點數據,能夠更改成其餘對結點的操做
  50. while(p->rtag == Thread && p->rchild != t)
  51. {
  52. p = p->rchild;
  53. printf( "%c ", p->data);
  54. }
  55. p = p->rchild; //p進入其右子樹
  56. }
  57. return OK;
  58. }
  59. //中序遍歷進行中序線索化
  60. void InThreading(BiTree p)
  61. {
  62. if(p)
  63. {
  64. InThreading(p->lchild); //遞歸左子樹線索化
  65. if(!p->lchild) //沒有左孩子
  66. {
  67. p->ltag = Thread; //前驅線索
  68. p->lchild = pre; //左孩子指針指向前驅,這裏是第3步
  69. }
  70. if(!pre->rchild) //沒有右孩子
  71. {
  72. pre->rtag = Thread; //後繼線索
  73. pre->rchild = p; //前驅右孩子指針指向後繼(當前結點p)
  74. }
  75. pre = p;
  76. InThreading(p->rchild); //遞歸右子樹線索化
  77. }
  78. }
  79. //創建頭結點,中序線索二叉樹
  80. int InOrderThread_Head(BiTree *h, BiTree t)
  81. {
  82. (*h) = (BiTree) malloc( sizeof(BitNode));
  83. if((*h) == NULL)
  84. {
  85. return ERROR;
  86. }
  87. (*h)->rchild = *h;
  88. (*h)->rtag = Link;
  89. if(!t) //若是爲NULL
  90. {
  91. (*h)->lchild = *h;
  92. (*h)->ltag = Link;
  93. }
  94. else
  95. {
  96. pre = *h;
  97. (*h)->lchild = t; //第一步
  98. (*h)->ltag = Link;
  99. InThreading(t); //找到最後一個結點
  100. pre->rchild = *h; //第四步
  101. pre->rtag = Thread;
  102. (*h)->rchild = pre; //第二步
  103. }
  104. }
  105. int main(int argc, char **argv)
  106. {
  107. BiTree t;
  108. BiTree temp;
  109. printf( "請輸入前序二叉樹的內容:\n");
  110. CreateTree(&t); //創建二叉樹
  111. InOrderThread_Head(&temp, t); //加入頭結點,併線索化
  112. printf( "輸出中序二叉樹的內容:\n");
  113. InOrderThraverse_Thr(temp);
  114. printf( "\n");
  115. return 0;
  116. }
轉自:[https://blog.csdn.net/u014492609/article/details/40477795](https://blog.csdn.net/u014492609/article/details/40477795)
相關文章
相關標籤/搜索