数据结构--树--线索二叉树(中序,前序,后序)

https://blog.csdn.net/DouBoomFly/article/details/71572601

线索二叉树

在遍历二叉树的时候,会有许多空指针域,这些空间不存储任何事物,白白浪费了内存的资源。
那么在做遍历的时候,提前记录下每个结点的前驱和后继,这样就更加节约了时间。
                 [ lchild ] [ LTag ] [ data ] [ RTag ] [ rchild ] 
LTag  = { 0 : lchild 域指示结点的左孩子 1 : lchild 域指示结点的前驱 } 
RTag = { 0 : rchild 域指示结点的右孩子 1 : rchild 域指示结点的后继 }  
以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中,指向结点前驱和后继的指针,叫做线索
加上线索的二叉树叫做线索二叉树(Threaded Binary Tree)
对二叉树以某种次序遍历使其变成线索二叉树的过程叫做线索化


★线索二叉树结构:

 
  1. #define TElemType char

  2. typedef enum{

  3. Link,Thread

  4. }PointerTag;//Link == 0 :指针 ,Thread == 1: 线索

  5. typedef struct BiThrNode{

  6. TElemType data;

  7. struct BiThrNode *lchild, *rchild; //左右孩子指针

  8. PointerTag LTag , RTag; //左右标志

  9. }BiThrNode, *BiThrTree;


                 [ lchild ] [ LTag ] [ data ] [ RTag ] [ rchild ] 
LTag  = { 0 : lchild 域指示结点的左孩子 1 : lchild 域指示结点的前驱 } 
RTag = { 0 : rchild 域指示结点的右孩子 1 : rchild 域指示结点的后继 }  


★线索化二叉树之前,咱们先把树建起来(用前序遍历建树)

 
  1. //char Vexch[20]={'H','D','A','$','$','C','$','B','$','$','G','F','$','E','$','$','$'};

  2. char Vexch[26]={'A','B','D','H','$','$','I','$','$','E','J','$','$','$','C','F','$','$','G','$','$'};

  3. int i=0;

  4. //二叉树的创建

  5. Status CreatBiThrTree(BiThrTree &T)

  6. {

  7. if(Vexch[i++]=='$') T=NULL;

  8. else

  9. {

  10. T= (BiThrTree)malloc(sizeof(BiThrNode));

  11. if(!T) return 0;

  12. T->data=Vexch[i-1];//生成根节点

  13. printf("%5c",T->data);

  14. T->LTag=Link;

  15. CreatBiThrTree(T->lchild);//创建左子树

  16. T->RTag=Link;

  17. CreatBiThrTree(T->rchild);//创建右子树

  18. }

  19. return 1;

  20. }

建立树为该树:
 

遍历visit()函数

 
  1. Status visit(TElemType e){

  2. printf("%5c",e);

  3. return OK;

  4. }

(1)中序遍历,线索二叉树

【让一棵树 直接变成一个线性表去遍历】
遍历 顺序为:H - >D - > I - > B  - > J - > E - > A - > F - > C - >  G
【建立二叉树头结点】(下面的代码没有循环,只是单纯的建立了一个头结点,连接上主体的树部分,方便遍历。)

 
  1. //建立头结点,中序线索二叉树

  2. Status InOrderThreading(BiThrTree &Thrt,BiThrTree T){

  3. //中序遍历二叉树T,并将其中序线索化,Thrt指向头结点。

  4. if(!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode))))

  5. return ERROR;

  6.  
  7. Thrt->RTag = Link; //建头结点

  8. Thrt->rchild = Thrt ; //右指针回指

  9. if(!T){

  10. Thrt->lchild = Thrt;

  11. Thrt->LTag = Link;

  12. }else{

  13. pre = Thrt ;

  14. Thrt->lchild = T;

  15. Thrt->LTag = Link;

  16. InThreading(T);

  17. pre->rchild = Thrt ;

  18. pre->RTag = Thread;

  19. Thrt->rchild = pre;

  20. }

  21. return OK;

  22. }

【解释】(pre永远指向上一个节点)
Thrt 就是 下图的空指针, 初始化 pre 为 这个空指针,完成下图的【1】【2】步骤
做完树的线索化后,pre 已经到了最后一个节点,那么就可以完成【3】【4】两个步骤了
如下图所示。
【1】让最左的结点,就是中序遍历时 第一个结点的左指针指向 空的头结点。
【第一个结点的左标记肯定是 Thread而不是 link ,这样就能找到最左的结点】 那么既然是线索Thread,就让他指向空的头结点(反正空着也是空着)
【2】头结点顺下来,从左子树开始找
【3】因为空节点的右标记是 Thread线索,那么让他指向最右的,最终结点。(反正空着也是空着)
【4】最终结点G的右线索指向头结点,标记着终结。
构成循环:
                    ↓   →  →  →  →   【  空的头结点】 ← ←  ←  ← ↑
                   H → D →  I → B  →  J → E →  A → F →  C →  G
 

★LTag  = { 0 : lchild 域指示结点的左孩子 1 : lchild 域指示结点的前驱 } RTag = { 0 : rchild 域指示结点的右孩子 1 : rchild 域指示结点的后继 }  
概括为:LTag=0(Link)【左孩子】,LTag=1(Thread)【前驱】;RTag=0(Link)【右孩子】 ,RTag=1(Thread)【后继】
【问】那么怎样的称为Link指针 ,怎样的 称为 Thread 线索

【答】可以这样理解,Link指针是本来建树的时候就有的,而Thread线索是为了线索化,而增添的。

【中序遍历线索化】

 
  1. BiThrTree pre; //全局变量,始终指向刚刚访问过的结点。

  2. void InThreading(BiThrTree p){

  3. if(p){

  4. InThreading(p->lchild); //左子树线索化

  5. if(!p->lchild){ //没有左孩子

  6. p->LTag = Thread; //前驱线索

  7. p->lchild = pre; //左孩子指针指向前驱

  8. }

  9. if(!pre->rchild){

  10. pre->RTag = Thread; //后继线索

  11. pre->rchild = p ; //前驱右孩子指针指向后继

  12. }

  13. pre = p;

  14. InThreading(p->rchild); //右子树线索化

  15. }

  16. }

【解释】
首先,很明显:中序遍历线索化,其实也是基于中序遍历的。(从代码中可以看出)
也是先左,再中,后右(左 > 中 > 右)
只是在中间部分,对结点的处理的时候,有些不一样。1、中序遍历时是输出。2、而现在我们把它替换为 处理标记和指针。
介绍如何处理,很简单:
★没有左孩子,或者没有右孩子那肯定是线索,而不是指针
★因为是中序,遍历肯定是从左到右,那么左边的线索肯定是指向前驱的,右边的线索肯定是指向后继的。


【中序遍历】

 
  1. //中序 遍历线索二叉树

  2. Status InOrderTraverse_Thr(BiThrTree T ,Status(* visit)(TElemType e) ){

  3. //T指向头结点,头结点的左链lchild指向根节点,可参见线索化算法

  4. //中序遍历二叉线索树T的非递归算法,对每个数据元素调用函数visit

  5. BiThrTree p ;

  6. p = T->lchild; // p指向根节点

  7. while(p != T){ //空树 或者遍历结束时 p == T

  8. while(p->LTag == Link ) // 走到最左结点

  9. p = p->lchild;

  10. visit(p->data);

  11. while(p->RTag == Thread && p->rchild !=T){

  12. p = p->rchild ; // 若有右线索,

  13. visit(p->data);

  14. }

  15. p = p->rchild;

  16. }

  17. return OK;

  18. }

【解释】
最外面的while 从上面构造的时候就已经说明了,当p回到T的时候,那么就标记结束了。
里面的第一个 while(p->LTag == Link) 循环,走到最左结点
输出该节点
while(p->RTag == Thread && p->rchild !=T)  如果右边有线索,且指向的不是最后的根T, 优先按着线索走。
发现这里没有线索了,那么就继续往右孩子找。


【总的中序遍历线索二叉树代码】

 
  1. #include <iostream>

  2. #include <string.h>

  3. #include <cstdio>

  4. #include <stdlib.h>

  5. using namespace std;

  6. #define Status int

  7. #define OK 1

  8. #define ERROR 0

  9. #define TElemType char

  10. typedef enum{

  11. Link,Thread

  12. }PointerTag;//Link == 0 :指针 ,Thread == 1: 线索

  13. typedef struct BiThrNode{

  14. TElemType data;

  15. struct BiThrNode *lchild, *rchild; //左右孩子指针

  16. PointerTag LTag , RTag; //左右标志

  17. }BiThrNode, *BiThrTree;

  18.  
  19. //char Vexch[20]={'H','D','A','$','$','C','$','B','$','$','G','F','$','E','$','$','$'};

  20. char Vexch[26]={'A','B','D','H','$','$','I','$','$','E','J','$','$','$','C','F','$','$','G','$','$'};

  21. int i=0;

  22. //二叉树的创建

  23. Status CreatBiThrTree(BiThrTree &T)

  24. {

  25. if(Vexch[i++]=='$') T=NULL;

  26. else

  27. {

  28. T= (BiThrTree)malloc(sizeof(BiThrNode));

  29. if(!T) return 0;

  30. T->data=Vexch[i-1];//生成根节点

  31. printf("%5c",T->data);

  32. T->LTag=Link;

  33. CreatBiThrTree(T->lchild);//创建左子树

  34. T->RTag=Link;

  35. CreatBiThrTree(T->rchild);//创建右子树

  36. }

  37. return 1;

  38. }

  39. Status visit(TElemType e){

  40. printf("%5c",e);

  41. return OK;

  42. }

  43.  
  44. BiThrTree pre; //全局变量,始终指向刚刚访问过的结点。

  45. void InThreading(BiThrTree p){

  46. if(p){

  47. InThreading(p->lchild); //左子树线索化

  48. if(!p->lchild){ //没有左孩子

  49. p->LTag = Thread; //前驱线索

  50. p->lchild = pre; //左孩子指针指向前驱

  51. }

  52. if(!pre->rchild){

  53. pre->RTag = Thread; //后继线索

  54. pre->rchild = p ; //前驱右孩子指针指向后继

  55. }

  56. pre = p;

  57. InThreading(p->rchild); //右子树线索化

  58. }

  59. }

  60. //建立头结点,中序线索二叉树

  61. Status InOrderThreading(BiThrTree &Thrt,BiThrTree T){

  62. //中序遍历二叉树T,并将其中序线索化,Thrt指向头结点。

  63. if(!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode))))

  64. return ERROR;

  65.  
  66. Thrt->RTag = Link; //建头结点

  67. Thrt->rchild = Thrt ; //右指针回指

  68. if(!T){

  69. Thrt->lchild = Thrt;

  70. Thrt->LTag = Link;

  71. }else{

  72. pre = Thrt ;

  73. Thrt->lchild = T;

  74. Thrt->LTag = Link;

  75. InThreading(T);

  76. pre->rchild = Thrt ;

  77. pre->RTag = Thread;

  78. Thrt->rchild = pre;

  79. }

  80. return OK;

  81. }

  82. //中序 遍历线索二叉树

  83. Status InOrderTraverse_Thr(BiThrTree T ,Status(* visit)(TElemType e) ){

  84. //T指向头结点,头结点的左链lchild指向根节点,可参见线索化算法

  85. //中序遍历二叉线索树T的非递归算法,对每个数据元素调用函数visit

  86. BiThrTree p ;

  87. p = T->lchild; // p指向根节点

  88. while(p != T){ //空树 或者遍历结束时 p == T

  89. while(p->LTag == Link ) // 走到最左结点

  90. p = p->lchild;

  91. visit(p->data);

  92. while(p->RTag == Thread && p->rchild !=T){

  93. p = p->rchild ; // 若有右线索,

  94. visit(p->data);

  95. }

  96. p = p->rchild;

  97. }

  98. return OK;

  99. }

  100. int main()

  101. {

  102. BiThrTree T, inorderT;

  103. printf("创建树\n");

  104. CreatBiThrTree(T);

  105. printf("\n中序遍历线索二叉树\n");

  106. InOrderThreading(inorderT , T);

  107. InOrderTraverse_Thr(inorderT , visit);

  108. printf("\n");

  109. return 0;

  110. }


 

(2)前序遍历,线索二叉树

【前序遍历二叉树线索化】

 
  1. BiThrTree pre; //全局变量,始终指向刚刚访问过的结点。

  2. void PreThreading(BiThrTree p){

  3. if(p){

  4. if(!p->lchild){ //没有左孩子

  5. p->LTag = Thread; //前驱线索

  6. p->lchild = pre; //左孩子指针指向前驱

  7. }

  8. if(!pre->rchild && pre){

  9. pre->RTag = Thread; //后继线索

  10. pre->rchild = p ; //前驱右孩子指针指向后继

  11. }

  12. pre = p;

  13. if(p->LTag == Link)

  14. PreThreading(p->lchild); //左子树线索化

  15. if(p->RTag == Link)

  16. PreThreading(p->rchild); //右子树线索化

  17. }

  18. }

【建立头结点】(和中序遍历一样)

 
  1. //建立头结点,前序线索二叉树

  2. Status PreOrderThreading(BiThrTree &Thrt,BiThrTree T){

  3. //前序遍历二叉树T,并将其前序线索化,Thrt指向头结点。

  4. if(!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode))))

  5. return ERROR;

  6.  
  7. Thrt->RTag = Thread; //建头结点

  8. Thrt->rchild = Thrt ; //右指针回指

  9. Thrt->LTag = Link;

  10. if(!T){

  11. Thrt->lchild = Thrt;

  12. }else{

  13. Thrt->lchild = T;

  14. pre = Thrt ;

  15. PreThreading(T);

  16. pre->rchild = Thrt ;

  17. pre->RTag = Thread;

  18. Thrt->rchild = pre;

  19. }

  20. return OK;

  21. }

                                                                               ↓  ← 【  空的头结点  】 ← ←  ←  ←  ←  ←  ←   ↑
                                                                               A → B →  D → H  →  I →  E →  J → C →  F →  G


   

         1、A的直接前驱

          ㈠若LTag 的值为1,那么LChild 所指结点就是直接前驱

          ㈡若LTag 的值为0,那么

                 ⒈若A为双亲左儿子,那么直接前驱就是A的双亲结点

                 ⒉若A为双亲右儿子,那么直接前驱就是A的双亲左儿子

         2、A的直接后继

          ㈠若RTag 的值为1,那么RChild 所指结点就是直接后继

          ㈡若RTag 的值为0,那么

                  ⒈若LTag 的值为0,那么直接后继就是其左儿子。

                  ⒉若LTag 的值为1,那么直接后继就是其右儿子。


【前序遍历二叉树】

 
  1. //前序 遍历线索二叉树

  2. Status PreOrderTraverse_Thr(BiThrTree T ,Status(* visit)(TElemType e) ){

  3. //T指向头结点,头结点的左链lchild指向根节点,可参见线索化算法

  4. //前序遍历二叉线索树T的非递归算法,对每个数据元素调用函数visit

  5. BiThrTree p ;

  6. p = T->lchild; // p指向根节点

  7. while(p != T){ //空树 或者遍历结束时 p == T

  8. visit(p->data);

  9. if(p->LTag == Link)

  10. p = p->lchild;

  11. else

  12. p = p->rchild;

  13. }

  14. return OK;

  15. }

【总的前序遍历线索二叉树代码】

 
  1. #include <iostream>

  2. #include <string.h>

  3. #include <cstdio>

  4. #include <stdlib.h>

  5. using namespace std;

  6. #define Status int

  7. #define OK 1

  8. #define ERROR 0

  9. #define TElemType char

  10.  
  11. typedef enum{

  12. Link,Thread

  13. }PointerTag;//Link == 0 :指针 ,Thread == 1: 线索

  14. typedef struct BiThrNode{

  15. TElemType data;

  16. struct BiThrNode *lchild, *rchild; //左右孩子指针

  17. PointerTag LTag , RTag; //左右标志

  18. }BiThrNode, *BiThrTree;

  19.  
  20. //char Vexch[20]={'H','D','A','$','$','C','$','B','$','$','G','F','$','E','$','$','$'};

  21. char Vexch[26]={'A','B','D','H','$','$','I','$','$','E','J','$','$','$','C','F','$','$','G','$','$'};

  22. int i=0;

  23. //二叉树的创建

  24. Status CreatBiThrTree(BiThrTree &T)

  25. {

  26. if(Vexch[i++]=='$') T=NULL;

  27. else

  28. {

  29. T= (BiThrTree)malloc(sizeof(BiThrNode));

  30. if(!T) return 0;

  31. T->data=Vexch[i-1];//生成根节点

  32. printf("%5c",T->data);

  33. T->LTag=Link;

  34. CreatBiThrTree(T->lchild);//创建左子树

  35. T->RTag=Link;

  36. CreatBiThrTree(T->rchild);//创建右子树

  37. }

  38. return 1;

  39. }

  40. Status visit(TElemType e){

  41. printf("%5c",e);

  42. return OK;

  43. }

  44.  
  45. BiThrTree pre; //全局变量,始终指向刚刚访问过的结点。

  46. void PreThreading(BiThrTree p){

  47. if(p){

  48. if(!p->lchild){ //没有左孩子

  49. p->LTag = Thread; //前驱线索

  50. p->lchild = pre; //左孩子指针指向前驱

  51. }

  52. if(!pre->rchild){

  53. pre->RTag = Thread; //后继线索

  54. pre->rchild = p ; //前驱右孩子指针指向后继

  55. }

  56. pre = p;

  57. if(p->LTag == Link)

  58. PreThreading(p->lchild); //左子树线索化

  59. if(p->RTag == Link)

  60. PreThreading(p->rchild); //右子树线索化

  61. }

  62. }

  63. //建立头结点,前序线索二叉树

  64. Status PreOrderThreading(BiThrTree &Thrt,BiThrTree T){

  65. //前序遍历二叉树T,并将其前序线索化,Thrt指向头结点。

  66. if(!(Thrt = (BiThrTree)malloc(sizeof(BiThrNode))))

  67. return ERROR;

  68.  
  69. Thrt->RTag = Thread; //建头结点

  70. Thrt->rchild = Thrt ; //右指针回指

  71. Thrt->LTag = Link;

  72. if(!T){

  73. Thrt->lchild = Thrt;

  74. }else{

  75. Thrt->lchild = T;

  76. pre = Thrt ;

  77. PreThreading(T);

  78. pre->rchild = Thrt ;

  79. pre->RTag = Thread;

  80. Thrt->rchild = pre;

  81. }

  82. return OK;

  83. }

  84. //前序 遍历线索二叉树

  85. Status PreOrderTraverse_Thr(BiThrTree T ,Status(* visit)(TElemType e) ){

  86. //T指向头结点,头结点的左链lchild指向根节点,可参见线索化算法

  87. //前序遍历二叉线索树T的非递归算法,对每个数据元素调用函数visit

  88. BiThrTree p ;

  89. p = T->lchild; // p指向根节点

  90. while(p != T){ //空树 或者遍历结束时 p == T

  91. visit(p->data);

  92. if(p->LTag == Link)

  93. p = p->lchild;

  94. else

  95. p = p->rchild;

  96. }

  97. return OK;

  98. }

  99. int main()

  100. {

  101. BiThrTree T, PreT;

  102. printf("创建树\n");

  103. CreatBiThrTree(T);

  104. printf("\n前序遍历线索二叉树\n");

  105. PreOrderThreading(PreT , T);

  106. PreOrderTraverse_Thr(PreT , visit);

  107. printf("\n");

  108. return 0;

  109. }

(3)后序遍历,线索二叉树

【后序遍历线索二叉树时,需要一个parent 指针,所以建树的时候与上面两个有所不同】

 
  1. #define TElemType char

  2. typedef enum{

  3. Link,Thread

  4. }PointerTag;//Link == 0 :指针 ,Thread == 1: 线索

  5. typedef struct BiThrNode{

  6. TElemType data;

  7. struct BiThrNode *lchild, *rchild; //左右孩子指针

  8. struct BiThrNode *parent;

  9. PointerTag LTag , RTag; //左右标志

  10. }BiThrNode, *BiThrTree;

  11. BiThrTree pre; //全局变量,始终指向刚刚访问过的结点。

  12.  
  13. Status visit(TElemType e){

  14. printf("%5c",e);

  15. return OK;

  16. }

  17. //char Vexch[20]={'H','D','A','$','$','C','$','B','$','$','G','F','$','E','$','$','$'};

  18. char Vexch[26]={'A','B','D','H','$','$','I','$','$','E','J','$','$','$','C','F','$','$','G','$','$'};

  19. int i=0;

  20. //二叉树的创建

  21. Status CreatBiThrTree(BiThrTree &T,BiThrTree &p)

  22. {

  23. if(Vexch[i++]=='$') T=NULL;

  24. else

  25. {

  26. T= (BiThrTree)malloc(sizeof(BiThrNode));

  27. if(!T) return 0;

  28. T->data=Vexch[i-1];//生成根节点

  29. T->parent = p;//指回原来的结点

  30. visit(T->data);

  31. T->LTag=Link;

  32. CreatBiThrTree(T->lchild,T);//创建左子树

  33. T->RTag=Link;

  34. CreatBiThrTree(T->rchild,T);//创建右子树

  35. }

  36. return 1;

  37. }

【后序遍历二叉树线索化】

 
  1. void PostThreading(BiThrTree p){

  2. if(p){

  3. PostThreading(p->lchild); //左子树线索化

  4. PostThreading(p->rchild); //右子树线索化

  5. if(!p->lchild){ //没有左孩子

  6. p->LTag = Thread; //前驱线索

  7. p->lchild = pre; //左孩子指针指向前驱

  8. }

  9. if(pre && !pre->rchild){

  10. pre->RTag = Thread; //后继线索

  11. pre->rchild = p ; //前驱右孩子指针指向后继

  12. }

  13. pre = p;

  14. }

  15. }

【建立头结点】(这里就不建头结点了,因为入口是总根节点,出口也是总根节点)

   

         1、A的直接前驱

          ㈠若LTag 的值为1,那么A的直接前驱为LChild所指结点

          ㈡若LTag 的值为0,那么

                  ⒈若有左儿子,那么直接前驱就是A的左儿子。

                  ⒉若有右儿子,那么直接前驱就是A的右儿子。

         2、A的直接后继

          ㈠若结点A是二叉树的根,则其后继为空

          ㈡若结点A是其双亲的右儿子,或是双亲的左孩子且其双亲没有左子树没有右子树,则其后继即为双亲结点

          ㈢若结点A是其双亲的左儿子,且双亲有右子树,则其后继为双亲的右子树上按后序遍历列出来的第一个结点。



【后序遍历二叉树】

 
  1. //后序 遍历线索二叉树

  2. Status PostOrderTraverse_Thr(BiThrTree T ,Status(* visit)(TElemType e) ){

  3. BiThrTree p ;

  4. p = T; // p指向根节点

  5. pre=NULL;

  6. while(p != NULL){ //空树 或者遍历结束时 p == T

  7. while(p->LTag == Link ) // 走到最左结点 ||左结点

  8. p = p->lchild;

  9.  
  10. while(p->RTag == Thread ){ //访问后继 ||右结点

  11. visit(p->data);

  12. pre = p;

  13. p = p->rchild ;

  14. }

  15. if(p == T){ //是否是最后根节点

  16. visit(p->data);

  17. break;

  18. }

  19. while(p && p->rchild == pre ){ //访问根 ||根节点

  20. visit(p->data);

  21. pre = p;

  22. p = p->parent;

  23. }

  24. if(p && p->RTag == Link)

  25. p = p->rchild;

  26. }

  27. return OK;

  28. }


【总的后序遍历线索二叉树代码】

 
  1. #include <iostream>

  2. #include <string.h>

  3. #include <cstdio>

  4. #include <stdlib.h>

  5. using namespace std;

  6. #define Status int

  7. #define OK 1

  8. #define ERROR 0

  9. #define TElemType char

  10. typedef enum{

  11. Link,Thread

  12. }PointerTag;//Link == 0 :指针 ,Thread == 1: 线索

  13. typedef struct BiThrNode{

  14. TElemType data;

  15. struct BiThrNode *lchild, *rchild; //左右孩子指针

  16. struct BiThrNode *parent;

  17. PointerTag LTag , RTag; //左右标志

  18. }BiThrNode, *BiThrTree;

  19. BiThrTree pre; //全局变量,始终指向刚刚访问过的结点。

  20.  
  21. Status visit(TElemType e){

  22. printf("%5c",e);

  23. return OK;

  24. }

  25. //char Vexch[20]={'H','D','A','$','$','C','$','B','$','$','G','F','$','E','$','$','$'};

  26. char Vexch[26]={'A','B','D','H','$','$','I','$','$','E','J','$','$','$','C','F','$','$','G','$','$'};

  27. int i=0;

  28. //二叉树的创建

  29. Status CreatBiThrTree(BiThrTree &T,BiThrTree &p)

  30. {

  31. if(Vexch[i++]=='$') T=NULL;

  32. else

  33. {

  34. T= (BiThrTree)malloc(sizeof(BiThrNode));

  35. if(!T) return 0;

  36. T->data=Vexch[i-1];//生成根节点

  37. T->parent = p;

  38. visit(T->data);

  39. T->LTag=Link;

  40. CreatBiThrTree(T->lchild,T);//创建左子树

  41. T->RTag=Link;

  42. CreatBiThrTree(T->rchild,T);//创建右子树

  43. }

  44. return 1;

  45. }

  46.  
  47. void PostThreading(BiThrTree p){

  48. if(p){

  49. PostThreading(p->lchild); //左子树线索化

  50. PostThreading(p->rchild); //右子树线索化

  51. if(!p->lchild){ //没有左孩子

  52. p->LTag = Thread; //前驱线索

  53. p->lchild = pre; //左孩子指针指向前驱

  54. }

  55. if(pre && !pre->rchild){

  56. pre->RTag = Thread; //后继线索

  57. pre->rchild = p ; //前驱右孩子指针指向后继

  58. }

  59. pre = p;

  60. }

  61. }

  62. //后序 遍历线索二叉树

  63. Status PostOrderTraverse_Thr(BiThrTree T ,Status(* visit)(TElemType e) ){

  64. BiThrTree p ;

  65. p = T; // p指向根节点

  66. pre=NULL;

  67. while(p != NULL){ //空树 或者遍历结束时 p == T

  68. while(p->LTag == Link ) // 走到最左结点 ||左结点

  69. p = p->lchild;

  70.  
  71. while(p->RTag == Thread ){ //访问后继 ||右结点

  72. visit(p->data);

  73. pre = p;

  74. p = p->rchild ;

  75. }

  76. if(p == T){ //是否是最后根节点

  77. visit(p->data);

  78. break;

  79. }

  80. while(p && p->rchild == pre ){ //访问根 ||根节点

  81. visit(p->data);

  82. pre = p;

  83. p = p->parent;

  84. }

  85. if(p && p->RTag == Link)

  86. p = p->rchild;

  87. }

  88. return OK;

  89. }

  90. int main()

  91. {

  92. BiThrTree PostT;

  93. printf("创建树\n");

  94. pre = NULL;

  95. CreatBiThrTree(PostT,pre);

  96. printf("\n后序遍历线索二叉树\n");

  97. PostThreading(PostT);

  98. PostOrderTraverse_Thr(PostT , visit);

  99. printf("\n");

  100. return 0;

  101. }

总结:
【问】为什么用先序遍历建树后,可以用来中序遍历线索化?
【答】先序遍历建树,只是一种建树方式(当然可以用别的方法来建树,但是数组里的顺序可能就要变化了),建完树后,跟后面线索化无关。


【问】为什么中序遍历,先序遍历,后序遍历在线索化的时候,要用不同的线索化?
【答】因为中序,先序,后序,他们的前驱和后继是不一样的,根据代码也知道是不一样。
 

【问】对于做题,画已知二叉树的前序、中序、后序线索二叉树有什么技巧吗?

【答】可以先将 二叉树前序、中序、后序遍历 顺序写出来。再根据写出来的顺序对二叉树进行线索化。

【问】接上,线索化的时候这么乱,不知道线索改连到哪里?

【答】每个结点左右各有一个指针,除了用于建树的“蓝色”线之外,我们只看红色的线索这条线。每个结点只要是线索的部分,左边就是指向排在该结点之前的那个结点,右边就是指排在该节点之后的那个结点,这也就是为什么要先把遍历的顺序提前写好的原因。

猜你喜欢

转载自blog.csdn.net/nalnait/article/details/81091372