当前位置: 首页 > news >正文

线索二叉树

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

在中序遍历序列中求某一结点的前驱和后继的方法:(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("请输入前序二叉树的内容:\n");
125     CreateTree(&t);                 //建立二叉树
126     InOrderThread_Head(&temp, t);       //加入头结点,并线索化
127     printf("输出中序二叉树的内容:\n");
128     InOrderThraverse_Thr(temp);
129      
130     printf("\n");
131     return 0;
132 }

 

转载于:https://www.cnblogs.com/moomcake/p/10048192.html

相关文章:

  • [源码和文档分享]基于rabbitMQ的微服务架构消息组件设计与实现
  • TYPORA的使用手册
  • python 断言 assert
  • 爬虫(二)之scrapy框架
  • three.js入门系列之视角和辅助线
  • elementui 走马灯图片自适应
  • CSS浮动(一)---Float
  • mode_w
  • war包
  • Js获取操作系统版本 获得浏览器版本
  • [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper
  • Alpha阶段个人总结
  • BZOJ5091: [Lydsy1711月赛]摘苹果【期望DP】
  • RDIFramework.NET V3.3 Web版新增报表管理功能模块-重量级实用功能
  • /etc/skel 目录作用
  • 《剑指offer》分解让复杂问题更简单
  • 【翻译】babel对TC39装饰器草案的实现
  • 0x05 Python数据分析,Anaconda八斩刀
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • egg(89)--egg之redis的发布和订阅
  • javascript 哈希表
  • java中的hashCode
  • mongo索引构建
  • Nodejs和JavaWeb协助开发
  • PAT A1017 优先队列
  • session共享问题解决方案
  • ViewService——一种保证客户端与服务端同步的方法
  • windows-nginx-https-本地配置
  • 分类模型——Logistics Regression
  • 巧用 TypeScript (一)
  • 少走弯路,给Java 1~5 年程序员的建议
  • 线上 python http server profile 实践
  • 硬币翻转问题,区间操作
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • Python 之网络式编程
  • 数据库巡检项
  • #Linux(权限管理)
  • #pragma multi_compile #pragma shader_feature
  • ${factoryList }后面有空格不影响
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (ibm)Java 语言的 XPath API
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (二)JAVA使用POI操作excel
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (十三)Flask之特殊装饰器详解
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • *** 2003
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .net core 6 redis操作类
  • .NET 将多个程序集合并成单一程序集的 4+3 种方法
  • .Net 路由处理厉害了