线索二叉树

Posted moomcake

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线索二叉树相关的知识,希望对你有一定的参考价值。

将二叉树线索化,实际上就是将其变为一个循环链表,下面的代码是采用中序的线索化,遍历也是中序遍历,都是基于中序的。

在中序遍历序列中求某一结点的前驱和后继的方法:(1)求某一结点的后继:如果所考虑的结点有右孩子,那么就要从该右孩子开始,顺着右孩子的左孩子域找下去,一直到左孩子域为空为止,最后这个结点就是所考虑结点的后继;如果所考虑的结点没有右孩子,那么就要遍历二叉树。(2)求某一结点前驱:如果所考虑结点有左孩子,那么就要从该左孩子开始,顺着该左孩子的右孩子链域找下去,一直到右孩子的链域为空为止,最后这个结点就是所考虑结点的前驱;然后该结点没有左孩子,那么就要遍历二叉树。

下面代码中还有一个要说的就是用来辅助搜索的Address结构体数组,一开始我本来是乡写一个函数,直接就是在遍历的过程中搜索指定数值的结点,但是有时对有时错,搞了一个下午都不知怎么搞好,就放弃了,采用这个结构体数组的方法,简单一些,那个等以后老师讲的时候再看看老师会怎么实现吧,可能会有灵感。
技术分享图片

中序遍历,建立线索二叉树

  1 #include<iostream>
  2 using namespace std;
  3  
  4 class ThreadTree;//线索二叉树
  5  
  6 class Thrnode
  7 {
  8 private:
  9     int data;
 10     int ltag;    //ltag为0:表示lchild为一般二叉树的指针,指向其左子树
 11     int rtag;    //ltag为1:表示lchild为指向此结点的前驱的指针,rtag同理
 12     Thrnode *lchild;
 13     Thrnode *rchild;
 14     
 15     friend class ThreadTree;
 16 };
 17  
 18 int num=0;//记录结点数目
 19 struct Address        //辅助搜索
 20 {
 21     int data;
 22     Thrnode *p;
 23 }ad[100];
 24  
 25 class ThreadTree
 26 {
 27 private:
 28     Thrnode *root;
 29 public:
 30     ThreadTree()
 31     {
 32         root=0;
 33     }
 34  
 35     Thrnode *Get_Root()
 36     {
 37         return root;
 38     }
 39  
 40     int Get_data(Thrnode *p)
 41     {
 42         return p->data;
 43     }
 44  
 45     Thrnode *Create_Tree(Thrnode *r)    //先序建立二叉树
 46     {
 47         int d;
 48         cout<<"输入数据(0代表空):";
 49         cin>>d;
 50         if(d==0)
 51             return 0;
 52         else
 53         {
 54             num++;
 55             r=new Thrnode;
 56             r->ltag=0;
 57             r->rtag=0;
 58             r->data=d;
 59             r->lchild=Create_Tree(r->lchild);
 60             r->rchild=Create_Tree(r->rchild);
 61         }
 62         root=r;
 63         return r;
 64     }
 65  
 66     void In_Threading(Thrnode *p,Thrnode *&pre)
 67     {
 68         //以指针p所指向的二叉树进行中序遍历,遍历过程中进行线索化
 69         //pre指针是p的前驱指针
 70         if(p)
 71         {
 72             In_Threading(p->lchild,pre);//左子树线索化
 73             if(!p->lchild) //若p的左子树为空,给p结点加前驱线索
 74             {
 75                 p->ltag=1;
 76                 p->lchild=pre;
 77             }
 78             else
 79                 p->ltag=0;
 80             if(pre && !pre->rchild)
 81             {
 82                 pre->rtag=1;
 83                 pre->rchild=p;
 84             }
 85             pre=p;
 86             In_Threading(p->rchild,pre); //右子树线索化
 87         }
 88  
 89     }
 90  
 91     Thrnode *InOrder_Threading(Thrnode *r) //将二叉树线索化,按中序遍历的顺序
 92     {
 93         //遍历二叉树,并将其中序线索化,其中,Thrt指针指向头结点,r指向根节点
 94         Thrnode *pre=NULL;
 95         Thrnode *Thrt;
 96         Thrt=new Thrnode;
 97         Thrt->ltag=0;
 98         Thrt->rtag=1;
 99         Thrt->rchild=Thrt;//初始化时,让头结点的右指针指向自己
100         if(!r)
101             Thrt->lchild=Thrt;    //若为空树,左指针也指回自己
102         else
103         {
104             Thrt->lchild=r;
105             pre=Thrt;
106             In_Threading(r,pre);    //pre是r的前驱指针
107  
108             //从上面的函数出来时,pre指在最后一个结点处
109             pre->rchild=Thrt;
110             pre->rtag=1;
111             //最后一个结点的线索化
112  
113             Thrt->rchild=pre;
114         }
115         cout<<"二叉树线索化 完成~~"<<endl;
116         return Thrt;
117     }
118     
119     void InOrder_Thr(Thrnode *Thrt)    //中序遍历线索二叉树
120     {
121         //遍历的同时,将相对应的值和结点指针存入结构体,便于搜索
122         int count=0;
123         Thrnode *r;
124         r=Thrt->lchild;
125         while(r!=Thrt)
126         {
127             while(r->ltag==0)
128                 r=r->lchild;
129             cout<<r->data<<"  ";
130             ad[count].data=r->data;
131             ad[count].p=r;
132             count++;
133             while(r->rtag==1 && r->rchild!=Thrt)
134             {
135                 r=r->rchild;
136                 cout<<r->data<<"  ";
137                 ad[count].data=r->data;
138                 ad[count].p=r;
139                 count++;
140             }
141             r=r->rchild;
142         }
143     }
144  
145     Thrnode *Search(int d)    //搜索
146     {
147         for(int i=0;i<num;i++)
148             if(ad[i].data==d)
149                 return ad[i].p;
150     }
151  
152     void Prior_Thr(Thrnode *Thrt,Thrnode *t,Thrnode *&p)  //求结点前驱
153     {
154         //p返回结点t的前驱
155         p=t->lchild;
156         if(p==Thrt)
157         {
158             cout<<"给定的结点是第一个结点,不存在前驱"<<endl;
159             return ;
160         }
161         if(t->ltag==0)
162             while(p->rtag==0)
163                 p=p->rchild;
164     }
165  
166     void Next_Thr(Thrnode *Thrt,Thrnode *t,Thrnode *&p)    //求结点后继
167     {
168         p=t->rchild;
169         if(p==Thrt)
170         {
171             cout<<"该结点为最后一个结点,无后继"<<endl;
172             return ;
173         }
174         if(p->rtag==0)
175             while(p->ltag==0)
176                 p=p->lchild;
177     }
178  
179     void Insert_Lchild(Thrnode *Thrt,Thrnode *t,int d)    //作为左孩子插入
180     {
181         /*
182         将结点值为d的结点插入t后面,作为t的左孩子
183         如果t本来的无左孩子,那么直接插入即可
184         如果t有左孩子,那么:
185         t的左孩子在新的节点q插入后,作为q的左孩子,因此q->ltag=0;
186         将新结点q作为t的左孩子
187         求出新的结点q的前驱,修改q的前驱结点的rchild域,使它的后继为q
188         */
189         
190         Thrnode *q=new Thrnode;
191         q->data=d;
192         
193         q->lchild=t->lchild;
194         q->ltag=t->ltag;
195         
196         q->rchild=t;
197         q->rtag=1;
198         
199         t->lchild=q;
200         t->ltag=0;
201  
202         if(q->ltag==0)
203         {
204             Thrnode *p;
205             Prior_Thr(Thrt,q,p);
206             p->rchild=q;
207         }
208         //若q->rtag为0,那么就要找出q的前继
209         cout<<"插入为结点值为:"<<t->data<<"的左孩子成功"<<endl;
210         num++;
211     }
212  
213     void Insert_Rchild(Thrnode *Thrt,Thrnode *t,int d)    //作为右孩子插入
214     {
215         //思路和上面的左孩子一样
216         Thrnode *q=new Thrnode;
217         q->data=d;
218         
219         q->rchild=t->rchild;
220         q->rtag=t->rtag;
221  
222         q->lchild=t;
223         q->ltag=1;
224  
225         t->rchild=q;
226         t->rtag=0;
227  
228         if(q->rtag==0)
229         {
230             Thrnode *p;
231             Next_Thr(Thrt,q,p);
232             p->lchild=q;
233         }
234         cout<<"插入为结点值:"<<t->data<<"的右孩子成功"<<endl;
235         num++;
236     }
237  
238 };
View
 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  
20         p = p->rchild;                         //p进入其右子树
21     }
22  
23     return OK;
24 }

 

Code

说明:


    (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  
  4 #define ERROR  0
  5 #define OK  1
  6  
  7 typedef enum{Link, Thread} PointerTag;      //link = 0表示指向左右孩子指针
  8                                             //Thread = 1表示指向前驱或后继的线索
  9 typedef struct BitNode
 10 {
 11     char data;                              //结点数据
 12     struct BitNode *lchild;                 //左右孩子指针
 13     struct BitNode *rchild; 
 14     PointerTag ltag;                        //左右标志
 15     PointerTag rtag;
 16 }BitNode, *BiTree;
 17  
 18 BiTree pre;                                 //全局变量,始终指向刚刚访问过的结点
 19  
 20 //前序创建二叉树
 21 void CreateTree(BiTree *t)
 22 {
 23     char ch;
 24     scanf("%c", &ch);
 25      
 26     if(ch == #)
 27     {
 28         *t = NULL;
 29     }
 30     else
 31     {
 32         (*t) = (BiTree)malloc(sizeof(BitNode));
 33         if((*t) == NULL)
 34         {
 35             return;
 36         }
 37         (*t)->data = ch;
 38         CreateTree(&((*t)->lchild));
 39         CreateTree(&((*t)->rchild));
 40     }
 41 }
 42  
 43  
 44 //t指向头结点,头结点左链lchild指向根结点,头结点右链rchild指向中序遍历的最后一个结点。
 45 //中序遍历二叉线索树表示的二叉树t
 46 int InOrderThraverse_Thr(BiTree t)
 47 {
 48     BiTree p;
 49     p = t->lchild;           //p指向根结点
 50     while(p != t)
 51     {
 52         while(p->ltag == Link)   //当ltag = 0时循环到中序序列的第一个结点
 53         {
 54             p = p->lchild;
 55         }
 56         printf("%c ", p->data);  //显示结点数据,可以更改为其他对结点的操作
 57         while(p->rtag == Thread && p->rchild != t)
 58         {
 59             p = p->rchild;
 60             printf("%c ", p->data);
 61         }
 62  
 63         p = p->rchild;           //p进入其右子树
 64     }
 65  
 66     return OK;
 67 }
 68  
 69 //中序遍历进行中序线索化
 70 void InThreading(BiTree p)
 71 {
 72     if(p)
 73     {
 74         InThreading(p->lchild);              //递归左子树线索化
 75         if(!p->lchild)                       //没有左孩子
 76         {
 77             p->ltag = Thread;                //前驱线索
 78             p->lchild = pre;             //左孩子指针指向前驱,这里是第3步
 79         }
 80         if(!pre->rchild)                 //没有右孩子
 81         {
 82             pre->rtag = Thread;              //后继线索
 83             pre->rchild = p;             //前驱右孩子指针指向后继(当前结点p)
 84         }
 85         pre = p;
 86  
 87         InThreading(p->rchild);              //递归右子树线索化
 88     }
 89 }
 90 //建立头结点,中序线索二叉树
 91 int InOrderThread_Head(BiTree *h, BiTree t)
 92 {
 93     (*h) = (BiTree)malloc(sizeof(BitNode));
 94     if((*h) == NULL)
 95     {
 96         return ERROR;
 97     }
 98  
 99     (*h)->rchild = *h;
100     (*h)->rtag = Link;
101  
102     if(!t)      //如果为NULL
103     {
104         (*h)->lchild = *h;
105         (*h)->ltag = Link;
106     }
107     else
108     {
109         pre = *h;
110         (*h)->lchild = t;        //第一步
111         (*h)->ltag = Link;
112         InThreading(t);         //找到最后一个结点
113         pre->rchild = *h;        //第四步
114         pre->rtag = Thread;
115         (*h)->rchild = pre;      //第二步
116     }
117 }
118  
119 int main(int argc, char **argv)
120 {
121     BiTree t;
122     BiTree temp;
123  
124     printf("请输入前序二叉树的内容:
");
125     CreateTree(&t);                 //建立二叉树
126     InOrderThread_Head(&temp, t);       //加入头结点,并线索化
127     printf("输出中序二叉树的内容:
");
128     InOrderThraverse_Thr(temp);
129      
130     printf("
");
131     return 0;
132 }

 

以上是关于线索二叉树的主要内容,如果未能解决你的问题,请参考以下文章

java实现线索化二叉树的前序中序后续的遍历(完整代码)

C#数据结构-线索化二叉树

线索二叉树

线索化二叉树

线索二叉树

线索二叉树