线索树的建立与遍历
在建立二叉树的时候发现,那些叶节点的左孩子和右孩子的指针域都是空的,浪费空间,这时候就可以将这些空间利用起来,让遍历更加方便,这就是线索树存在的原因,线索树实现完了之后其实会发现就是一个双向链表,那种遍历就容易的多了。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef char ElemType; //数据类型 5 typedef enum {Link,Thread} childTag; //Link表示结点,Thread表示线索 6 typedef struct bitNode 7 { 8 ElemType data; 9 struct bitNode *lchild,*rchild; 10 int ltag,rtag; 11 } bitNode ,*bitTree; 12 13 void create_tree(bitTree *T,char **arr); //创建二叉树 14 void pre_order_traverse(bitTree T,int level); //前序遍历二叉树 15 void in_order_threading(bitTree T); //中序遍历线索化 16 void in_thread(bitTree *P,bitTree T); //新加入一个头结点,让二叉树成一个封闭环 17 void in_order_traverse(bitTree T); //中序遍历二叉树(带有头结点) 18 void visit(bitTree T); //访问结点信息 19 20 bitTree pre; //表示上次刚刚访问的结点 21 22 int main() 23 { 24 bitTree P,T; 25 int level =1; //表示该结点的深度 26 char *arr="ab d ce "; //构造二叉树所需结点(按前序遍历方式输入), 只是实验数据, 也可以从键盘输入 27 create_tree(&T,&arr); //构造二叉树 28 printf("pre-order-traverse\n"); 29 pre_order_traverse(T,level); //前序遍历输出二叉树 30 printf("\nin-order-traverse\n"); 31 in_thread(&P,T); //二叉树线索化 32 in_order_traverse(P); //输出线索化后的二叉树 33 return 0; 34 } 35 36 /* 37 创建二叉树,其输入必须按照前序遍历的次序。 38 T:二叉树根节点 39 arr:按照前序遍历次序排列的各节点的值。无孩子结点时用空格代替 40 */ 41 void create_tree(bitTree *T,char **arr) 42 { 43 char c; 44 sscanf(*arr,"%c",&c); //读入一个结点值 45 (*arr)++; 46 if(' '==c) //如果是空格,表示空结点 47 { 48 *T=NULL; 49 } 50 else 51 { 52 *T=(bitTree)malloc(sizeof(bitNode)); //构造新结点 53 (*T)->data=c; 54 (*T)->ltag=Link; 55 (*T)->rtag=Link; 56 create_tree(&(*T)->lchild,arr);//构造新结点的左孩子 57 create_tree(&(*T)->rchild,arr);//构造新结点的右孩子 58 } 59 } 60 61 /* 62 前序遍历访问二叉树 63 */ 64 void pre_order_traverse(bitTree T,int level) 65 { 66 if(T) 67 { 68 visit(T); 69 pre_order_traverse(T->lchild,level+1); 70 pre_order_traverse(T->rchild,level+1); 71 } 72 } 73 74 /* 75 中序遍历二叉树,对其进行线索化 76 */ 77 void in_order_threading(bitTree T) 78 { 79 if(T) 80 { 81 in_order_threading(T->lchild); //左孩子线索化 82 if(!T->lchild) //如果左孩子为空,则将其指向直接前驱 83 { 84 T->lchild=pre; 85 T->ltag=Thread; 86 } 87 if(!pre->rchild) //如果上一个结点的右孩子为空,则将其指向直接后继。(注意:只有访问到下一个结点时,才会知道本结点的后继是谁) 88 { 89 pre->rchild=T; 90 pre->rtag=Thread; 91 } 92 pre=T; 93 in_order_threading(T->rchild); //右孩子线索化 94 } 95 } 96 97 /* 98 加入一个头结点,使二叉线索树成一个封闭环 99 P:带有头结点的二叉树。头结点的左孩子指向二叉树T;右孩子指向T树中的最后一个叶子结点 100 T:不带有头结点的二叉树。 101 */ 102 void in_thread(bitTree *P,bitTree T) 103 { 104 (*P)=(bitTree)malloc(sizeof(bitNode)); //构造新加入的头结点 105 (*P)->ltag=Link; 106 (*P)->rtag=Thread; 107 (*P)->rchild=*P; 108 if(!T) //如果二叉树为空,则P的孩子指向自己。 109 { 110 (*P)->lchild=*P; 111 } 112 else 113 { 114 (*P)->lchild=T; 115 pre=*P; 116 in_order_threading(T); //对二叉树进行线索化 117 (*P)->rchild=pre; //将头结点右孩子指向最后一个叶子结点 118 pre->rtag=Thread; //将最后一个叶子结点的右孩子指向头结点。这样,环就形成了。 119 pre->rchild=*P; 120 } 121 } 122 123 /* 124 非递归方式:中序遍历二叉树(树必须带有头结点,且已经线索化) 125 P:带有头结点的二叉树 126 */ 127 void in_order_traverse(bitTree P) 128 { 129 bitTree T; 130 T=P->lchild; 131 while(T!=P) //判断是否空树 132 { 133 while(T->ltag==Link) //从左孩子开始,直到叶子结点 134 { 135 T=T->lchild; 136 } 137 visit(T); 138 while(T->rtag==Thread && T->rchild!=P) //根据线索,访问后继结点。并且后继结点不是指向头结点的 139 { 140 T=T->rchild; 141 visit(T); 142 } 143 T=T->rchild; 144 } 145 } 146 147 /* 148 访问结点信息 149 */ 150 void visit(bitTree T) 151 { 152 printf("%c ",T->data); 153 }