一、概念

传统的二叉树,只表示出了二叉树的父子关系,而缺少一种线性的联系,因此引入了二叉线索树,以便加快查找前缀和后继的速度,主要是利用二叉树中没有利用到的节点指针。如果指针没有指向节点,就让左向指针指向节点的前驱节点,让右向指针指向节点的后继节点,由于做出了改动,所以需要在每个节点的结构体里面添加两个标记,用于区分指向的到底是节点还是前驱后继。

struct TNode{int data;int ltag,rtag;struct TNode *left;struct TNode *right;
};

根据遍历方式的不同,节点的前驱后继也会不一样,所以延伸出三种构建二叉线索树的方法,本质上构建二叉线索树的过程就是遍历一遍二叉树的过程,在遍历过程中记录前一个节点,以此来实现前驱后继的指向。

线索化的过程一定是基于前驱后缀的,理顺好前驱后缀的关系,把没有指向的空指针位置做好调整,就能得到线索化的二叉树。先画出图再以图为基础构建代码。

二、中序线索化

先从中序线索化开始,维护一个pre用于指向上一个节点,初始值为NULL,从中序遍历的过程可以看出,先访问的节点应该是整个二叉树最左下的节点,所以先采用递归的方式,一直向左下方向移动,当没有左孩子的时候遍历到了最左下的节点,此时最左下节点的左孩子指针根据定义应该指向该节点的前驱,即pre指针所指的节点。由于当前pre指向的是NULL,所以不对其做操作,只有当pre指向的节点不为空且其右孩子指针空,才修改pre指针指向的节点的右孩子指针让其指向当前节点,即找到了pre指针所指结点的后继节点。将pre移动到当前节点的位置,再遍历节点的右子树部分。重复这个过程直到全部节点遍历一次,最后将中序遍历的最后一个节点即最右下的节点的后继指向空。这样完成的只是线索化,但是输出时如果要利用添加的线索,就需要改一下代码。


void InThread(struct TNode * &p,struct TNode * &pre)
{if(p!=NULL){InThread(p->left,pre);if(p->left==NULL){p->left=pre;p->ltag=1;}if(pre!=NULL&&pre->right==NULL){pre->right=p;pre->rtag=1;}pre=p;InThread(p->right,pre);}
}
void creatInThread()
{struct TNode *pre;pre=NULL;if(t.root!=NULL){InThread(t.root,pre);pre->right=NULL;pre->rtag=1;}
}

输出时,根据中序的规则,先找到最左下的节点,之后如果节点右孩子指针指向的是后继元素,就直接移向后继节点,如果右孩子指针指向的是右孩子节点,那么就找右子树的最左边的节点,重复这个过程直到为空。其实在输出的时候,前驱是没什么卵用的,不可能根据前驱去找一个已经访问过的节点,一般是利用后继,如果有后继即直接跳转,没有就找右子树的最左下节点,相当于笨办法去找后继。

struct TNode * FirstNode(struct TNode *p)
{while(p->ltag==0)p=p->left;return p;
}
struct TNode * NextNode(struct TNode *p)
{if(p->rtag==0)return FirstNode(p->right);elsereturn p->right;
}
void InOrder()
{for(struct TNode *p=FirstNode(t.root);p!=NULL;p=NextNode(p))printf("%d ",p->data);
}

完整版代码如下,层序建树后中序线索化:

#include<bits/stdc++.h>
using namespace std;
struct Tree{struct TNode* root;int size;
};
struct Tree t;
struct TNode{int data;int ltag,rtag;struct TNode *left;struct TNode *right;
};
int num[1005];
int n;
void creat()
{queue<TNode *> q;struct TNode *temp;temp=(struct TNode*)malloc(sizeof(struct TNode));temp->data=num[0];temp->left=NULL;temp->ltag=0;temp->right=NULL;temp->rtag=0;t.root=temp;q.push(temp);int i=1;while(i<n){temp=q.front();q.pop();temp->ltag=0;struct TNode *l;l=(struct TNode*)malloc(sizeof(struct TNode));l->data=num[i];l->left=NULL;l->ltag=0;l->right=NULL;l->rtag=0;i++;temp->left=l;q.push(l);if(i==n)break;temp->rtag=0;struct TNode *r;r=(struct TNode*)malloc(sizeof(struct TNode));r->data=num[i];r->left=NULL;r->ltag=0;r->right=NULL;r->rtag=0;i++;temp->right=r;q.push(r);}
}
void InThread(struct TNode * &p,struct TNode * &pre)
{if(p!=NULL){InThread(p->left,pre);if(p->left==NULL){p->left=pre;p->ltag=1;}if(pre!=NULL&&pre->right==NULL){pre->right=p;pre->rtag=1;}pre=p;InThread(p->right,pre);}
}
void creatInThread()
{struct TNode *pre;pre=NULL;if(t.root!=NULL){InThread(t.root,pre);pre->right=NULL;pre->rtag=1;}
}
struct TNode * FirstNode(struct TNode *p)
{while(p->ltag==0)p=p->left;return p;
}
struct TNode * NextNode(struct TNode *p)
{if(p->rtag==0)return FirstNode(p->right);elsereturn p->right;
}
void InOrder()
{for(struct TNode *p=FirstNode(t.root);p!=NULL;p=NextNode(p))printf("%d ",p->data);
}
int main()
{scanf("%d",&n);for(int i=0;i<n;i++)scanf("%d",&num[i]);creat();creatInThread();InOrder();return 0;
}

三、前序线索化

根据上一篇的总结,前序中序最好改了,换换顺序就可以,线索化也是这样,还是先从前序遍历的过程开始找,第一个访问的节点肯定是根节点,每次都先访问到达的节点的数据,所以不能像中序那样子直接上来就暴力移动,移动应该放在对当前节点的操作之后,顺序为:判断当前节点的有没有左孩子,没有则让其指向前驱,然后判断当前节点是不是谁的后继,之后将pre调整为当前节点,之后先左移递归再右移递归。这里一定要加一个判断,只有当左右孩子为节点时才移动,否则会出现死循环的现象。重复过程直到全部节点遍历一边,最后让最右下的节点指向空即可。

void InThread(struct TNode * &p,struct TNode * &pre)
{if(p!=NULL){if(p->left==NULL){p->left=pre;p->ltag=1;}if(pre!=NULL&&pre->right==NULL){pre->right=p;pre->rtag=1;}pre=p;if(pre->ltag==0)InThread(p->left,pre);if(pre->rtag==0)InThread(p->right,pre);}
}
void creatInThread()
{struct TNode *pre;pre=NULL;if(t.root!=NULL){InThread(t.root,pre);pre->right=NULL;pre->rtag=1;}
}

输出时,就比较简单了,从根节点开始,如果有左孩子节点就移向左孩子,没有就直接移向后继,因为前序遍历时左孩子的后继直接就是右孩子,不管右指针指向的是右孩子还是后继元素,都可以直接移向有指针的位置。

struct TNode * NextNode(struct TNode *p)
{if(p->ltag==0)return p->left;else return p->right;
}
void InOrder()
{for(struct TNode *p=t.root;p!=NULL;p=NextNode(p))printf("%d ",p->data);
}

完整代码如下,层序建树之后前序线索化:

#include<bits/stdc++.h>
using namespace std;
struct Tree{struct TNode* root;int size;
};
struct Tree t;
struct TNode{int data;int ltag,rtag;struct TNode *left;struct TNode *right;
};
int num[1005];
int n;
void creat()
{queue<TNode *> q;struct TNode *temp;temp=(struct TNode*)malloc(sizeof(struct TNode));temp->data=num[0];temp->left=NULL;temp->ltag=0;temp->right=NULL;temp->rtag=0;t.root=temp;q.push(temp);int i=1;while(i<n){temp=q.front();q.pop();temp->ltag=0;struct TNode *l;l=(struct TNode*)malloc(sizeof(struct TNode));l->data=num[i];l->left=NULL;l->ltag=0;l->right=NULL;l->rtag=0;i++;temp->left=l;q.push(l);if(i==n)break;temp->rtag=0;struct TNode *r;r=(struct TNode*)malloc(sizeof(struct TNode));r->data=num[i];r->left=NULL;r->ltag=0;r->right=NULL;r->rtag=0;i++;temp->right=r;q.push(r);}
}
void InThread(struct TNode * &p,struct TNode * &pre)
{if(p!=NULL){if(p->left==NULL){p->left=pre;p->ltag=1;}if(pre!=NULL&&pre->right==NULL){pre->right=p;pre->rtag=1;}pre=p;if(pre->ltag==0)InThread(p->left,pre);if(pre->rtag==0)InThread(p->right,pre);}
}
void creatInThread()
{struct TNode *pre;pre=NULL;if(t.root!=NULL){InThread(t.root,pre);pre->right=NULL;pre->rtag=1;}
}
struct TNode * NextNode(struct TNode *p)
{if(p->ltag==0)return p->left;else return p->right;
}
void InOrder()
{for(struct TNode *p=t.root;p!=NULL;p=NextNode(p))printf("%d ",p->data);
}
int main()
{scanf("%d",&n);for(int i=0;i<n;i++)scanf("%d",&num[i]);creat();creatInThread();InOrder();return 0;
}

四、后续线索化

后续线索化是最难实现的,这个不是很好表达,最好的办法就是画图来表示。

从图不难发现,和前序中序不一样的是,当访问完标号为2的节点的时候,想要找到其后继6是很难的,2号节点有左右孩子,必然不能通过前驱后继指针来直接移动,那么想找到6号节点,唯一的办法就是回到上一层,在转向右子树,但是找上一层又是个麻烦事,因为没有直接指向父节点的指针,只能从根节点从头开始找,这必然是个很费时间的过程,现有的知识解决不了,查了查别人的代码,发现是用带标志域的三叉链表作为存储结构,枉费了一个小时尝试返回上一层的代码。这里放一下改了很久的代码,可以实现三层的线索化,但是超过三层就炸了。

层序建树,不超过三层情况下后序线索化:

#include<bits/stdc++.h>
using namespace std;
struct Tree{struct TNode* root;int size;
};
struct Tree t;
struct TNode{int data;int ltag,rtag;struct TNode *left;struct TNode *right;
};
int num[1005];
int n;
void creat()
{queue<TNode *> q;struct TNode *temp;temp=(struct TNode*)malloc(sizeof(struct TNode));temp->data=num[0];temp->left=NULL;temp->ltag=0;temp->right=NULL;temp->rtag=0;t.root=temp;q.push(temp);int i=1;while(i<n){temp=q.front();q.pop();temp->ltag=0;struct TNode *l;l=(struct TNode*)malloc(sizeof(struct TNode));l->data=num[i];l->left=NULL;l->ltag=0;l->right=NULL;l->rtag=0;i++;temp->left=l;q.push(l);if(i==n)break;temp->rtag=0;struct TNode *r;r=(struct TNode*)malloc(sizeof(struct TNode));r->data=num[i];r->left=NULL;r->ltag=0;r->right=NULL;r->rtag=0;i++;temp->right=r;q.push(r);}
}
void InThread(struct TNode * &p,struct TNode * &pre)
{if(p!=NULL){InThread(p->left,pre);InThread(p->right,pre);if(p->left==NULL){p->left=pre;p->ltag=1;}if(pre!=NULL&&pre->right==NULL){pre->right=p;pre->rtag=1;}pre=p;}
}
void creatInThread()
{struct TNode *pre;pre=NULL;if(t.root!=NULL)InThread(t.root,pre);
}
struct TNode * FirstNode(struct TNode *p)
{while(p->ltag==0)p=p->left;return p;
}
struct TNode * Findup(struct TNode *p)
{struct TNode *s=t.root;   while(s->ltag==0){ if(s->left==p)return FirstNode(s->right);s=s->left;}s=t.root;if(s->right==p)return s;while(s->rtag==0){  if(s->right==p)return FirstNode(s->right);s=s->right;}
}
struct TNode * NextNode(struct TNode *p)
{if(p->rtag==0)return Findup(p);elsereturn p->right;
}
void InOrder()
{for(struct TNode *p=FirstNode(t.root);;p=NextNode(p)){printf("%d ",p->data);if(p==t.root)break;}}
int main()
{scanf("%d",&n);for(int i=0;i<n;i++)scanf("%d",&num[i]);creat();creatInThread();InOrder();return 0;
}

数据结构 5-3-3 二叉树的线索化相关推荐

  1. 二叉树线索化示意图_二叉树的线索化算法思想详解

    二叉树的线索化,这几天以来我很难掌握,今天终于想通了,哈哈,首先我们来看看二叉树线索化之后会变成什么样子,这里我们以图中的二叉树为例,图如下: 画的太糙,各位看官讲究着看吧- -.所谓二叉树的线索化, ...

  2. 二叉树的层序遍历和二叉树的线索化

    先根,后子树:先左子树,后右子树 二叉树的根节点 a 入队 a 的子树,根节点 b 和 c 分别入队 然后 b 的子树的根节点入队(为空) c 的子树的根节点入队 d 的子树的根节点入队(为空) e ...

  3. 线索二叉树的线索化、及遍历

    线索二叉树 遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序序列或中序序列或后序序列.这实际上是对一个非线性序列结构进行线性化操作,使每个结点(除了第一和最后一个)在这些 ...

  4. 二叉树在线索化后,仍不能有效求解的问题是()

    二叉树在线索化后,仍不能有效求解的问题是(D) A先序线索二叉树中求先序后继 B中序线索二叉树中求中序后继 C中序线索二叉树中求中序前驱 D后序线索二叉树中求后序后继 先序遍历(中左右).中序遍历(左 ...

  5. c语言实现二叉树的线索化和线索二叉树的遍历

    时构建线索二叉树的结构体 线索二叉树比平常的二叉树多了一个ltag域和一个rtag域,他们的作用在后面遍历时会说 #include<stdio.h> #include<stdlib. ...

  6. 二叉树线索化示意图_二叉树的线索化

    二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历.二叉树作为存储结构时,一个节点只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继.为了保存这种在 ...

  7. 算法与数据结构(三) 二叉树的遍历及其线索化(Swift版)

    前面两篇博客介绍了线性表的顺序存储与链式存储以及对应的操作,并且还聊了栈与队列的相关内容.本篇博客我们就继续聊数据结构的相关东西,并且所涉及的相关Demo依然使用面向对象语言Swift来表示.本篇博客 ...

  8. 数据结构-C语言版-二叉树中序线索化

    二叉树中的线索化问题,数据结构最核心.密集的考点之一:树,以中序二叉树的线索化 代码参考王道数据结构课程! #include<stdio.h> #include<stdlib.h&g ...

  9. 线索二叉树原理及前序、中序线索化(Java版)

    转载 原文地址:https://blog.csdn.net/UncleMing5371/article/details/54176252 一.线索二叉树原理 前面介绍二叉树原理及特殊二叉树文章中提到, ...

最新文章

  1. neo4j客户端下载
  2. ASP.NET MVC 概述
  3. C/C++写无控制台窗口程序
  4. oracle asin(),PLSQL ASIN用法及代码示例
  5. 小程序中实现滚动字幕
  6. python的内存分配
  7. 一卡通大冒险(hdu2512)
  8. C# ListView 简单命令例子
  9. jieba库词频统计_如何用python对《三国演义》、《红楼梦》等名著开展词云分析及字频统计、出场统计等工作。...
  10. 如何使用Vue集成其它UI组件?如何阅读UI官网?移动端组件Vant实例教程(熬夜干货)
  11. wordpress 伪静态nginx设置
  12. CHAPTER 28 VMX SUPPORT FOR ADDRESS TRANSLATION
  13. 到底什么是BFC、IFC、GFC和FFC,次奥?
  14. 如何使用低代码平台设计一套请假流程?
  15. 画论13 朱景玄《唐朝名画录》
  16. switch() 参数支持类型
  17. 算法精品讲解(2)——DP问题入门(适合零基础者,一看就会)
  18. sim卡无线上网公用服务器,云SIM卡池,云SIM卡解决方案,流量分发随身WIFI方案( 忻瑞科技)...
  19. 权限管理——用户认证和用户授权
  20. 内核自带的基于GPIO的LED驱动学习(三)

热门文章

  1. 【转】Android中APK安装过程及原理解析
  2. usb驱动自安装程序的制作
  3. 列出IIS上的虚拟目录和网站信息。
  4. 在Window上安装Mysql
  5. 【16】成对使用new和delete时要采取相同形式
  6. 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)
  7. 有鱼上钩!卖鱼!卖鱼!
  8. autograd手动仿真手记
  9. 〖Demo〗-- 模拟登录
  10. Dynamics CRM2016 Update or Create parentcustomerid in Contact using web api