大家好呀!
你们的大牛学长又回来了!
大牛学长真的是无处不在 ……
线索二叉树是很多学弟学妹一直嚷嚷着学不懂的问题,但是它也不像“AOV”、“AOE”、“BMP”、“哲学家进餐”等知识点那样高冷得完全让人摸不着头脑!
![108c12a8dd958da501405706cc282a3e.gif](https://i-blog.csdnimg.cn/blog_migrate/411fb21bd03e8c2204e0b76b86eeaba6.gif)
线索二叉树这样的知识点就像个磨人的小妖精,既让人捉摸不定,又给人想要征服它的欲望哈哈哈……
![4b7ab16aaee2b507755e0bcde910b4f8.png](https://i-blog.csdnimg.cn/blog_migrate/288fa461124fa338bad6cd673dca432c.jpeg)
今天学长就带大家一起搞定它!
走起~
01
前言
线索二叉树是二叉树的一个拓展结构,是基于二叉树的遍历这一知识点上拓展出来的一种特殊二叉树形式,在保留原有二叉树用两个指针域指向左右孩子结点的结构的同时,使用空指针域对于遍历序列的直接前驱和直接后继进行存储,以此来简化二叉树的遍历过程,但是注意,虽然简化了遍历过程,在线索二叉树上进行遍历操作时间复杂度仍然是O(n),因为仍然是对n个结点进行访问。
02
先导知识
(敲黑板!咱们先来复习复习一般的二叉树!)
• 二叉树的概念和数据结构
二叉树的基本组成:根结点、左子树、右子树。若能依次遍历这三部分,就是遍历了二叉树。
• 遍历二叉树 traversing binary tree:按某种路径访问且只访问一次结点
根据这三部分遍历顺序的不同分类:
DLR——先(根)序遍历。
LDR——中(根)序遍历。
LRD——后(根)序遍历。
按层次观察:
层序遍历:是从根结点开始遍历,按层次次序, “自上而下,从左至右”访问树中的各结点。
![89ee53a8a68542d236502ef61e34cd2d.png](https://i-blog.csdnimg.cn/blog_migrate/a5d9066352ca05a739fa905a27c36ef1.jpeg)
图中二叉树对应的遍历序列:
先序遍历:-+a*b-cd/ef
中序遍历:a+b*c-d-e/f
后序遍历:abcd-*+ef/-
层序遍历:-+/a*efb-cd
在掌握了二叉树的遍历的知识之后我们可以正式开始对于线索二叉树的学习。
03
知识背景
我们先考虑为什么要创造线索二叉树?
如果同学们有很好的掌握二叉树的遍历这一部分的知识,我们可以知道对于层序遍历来说我们需要借助队列对二叉树进行非递归实现,而对于先序遍历、中序遍历、后序遍历来说,我们可以递归实现也可以借助栈来非递归实现。
但是显然对于每一种经典遍历实现来说,对于任意一个结点p的直接前驱或是直接后继,我们都是要经过辅助数据结构比如队列或者栈的操作才能够得到的,并且如果我们要对一个二叉树进行多次遍历,每次单独的遍历过程都是相同的,也就是我们不能“记忆”动态遍历过程。这就像是我们第一次从家里出发上学,我们第一次可能进行了多次尝试才找到了一条路径,那么第二次上学的时候我们应该是根据上一次的记忆走上次的路径,而不是像第一次出发那样尝试所有的路。
线索二叉树的产生就是为了帮助我们“记忆”遍历序列。
线索二叉树是基于以下几个前提条件产生的:
1.已知对于某个二叉树进行某种遍历得到的序列。
2.以二叉链表作为存储结构的二叉树只能找到左右孩子信息,不能直接得到遍历序列中对应结点的前驱和后继。
3.二叉链表有n+1个空结点。这是由于在有n个结点的二叉树当中一共存在2n个指针域,而n个结点对应的树度数之和为n-1,也就是非空指针域数量为n-1,由此空结点数量为n+1。
因此我们考虑使用结点的空指针域来记录结点的前驱和后继
而由于线索二叉树记录的是不同的遍历序列,线索二叉树按照遍历方式来分类可以分为:先序线索二叉树、中序线索二叉树、后序线索二叉树(层序线索二叉树一般不会提及)
04
基本概念
线索:指向前驱和后继的指针
线索二叉树Threaded Binary Tree:加上线索的二叉树
线索化:以某种次序遍历使二叉树变为线索二叉树的过程
在线索树上遍历只需要(1)找到第一个结点(2)依次找结点直接后继直到其直接后继为空
数据结构
![833375ed9967e452961c86663b59ba6b.png](https://i-blog.csdnimg.cn/blog_migrate/e2eb20f0f6fdc4479f9c4ea84aab4145.png)
我们始终用结点p的左空结点指向p的直接前驱,用结点p的右空结点指向p的直接后继,用Tag来区分指向的是孩子还是线索。区分方法如下所示:
![08616d79a96cecce260a9251877abc5b.png](https://i-blog.csdnimg.cn/blog_migrate/ca1656aee19f1bfe53714689594ea858.jpeg)
这里给出了中序线索二叉树的示意图。当我们在画图的时候用虚线表示线索,用实线表示孩子。
![b159efd91ecdd192de3f2cc28ed4cf98.png](https://i-blog.csdnimg.cn/blog_migrate/630708081ca2e8012af464ad408c0ea6.jpeg)
对应的中序遍历序列为:a+b*c-d-e/f
线索化也就是构造过程是:
• a是中序遍历的第一个结点,并且左孩子为空,使得左指针指向NULL表明是第一个结点。
• +是中序遍历的第二个结点,是a的直接后继。a的右孩子为空,修改a的右指针为虚线,表明指向的+是a的直接后继;+的左孩子不为空因此不能进行修改。
• b是中序遍历的第三个结点,是+的直接后继。+的右孩子不为空,不能进行修改;b的左孩子为空,修改b的左指针为虚线,表明指向的+是b的直接前驱。
• 由此类推就能完成中序线索二叉树的构造过程。
相对应的,在中序线索二叉树上进行遍历:
• a的左指针指向NULL表明这是中序遍历的第一个结点。
• a的右指针为虚线,表明指向的是直接后继,表明+是a的直接后继是中序遍历的第二个结点。
• +的右指针为实线,表明指向的是孩子而不是直接后继,需要寻找直接后继,在中序遍历中根结点的直接后继为右子树的最左结点,因此对应的是b,b是中序遍历的第三个结点。
• 由此类推就能借助线索二叉树完成中序遍历过程。
线索二叉树的两个重点:线索化和遍历
线索化就是构造线索二叉树的过程,遍历则是通过线索二叉树完成遍历的过程。我们这篇文章重点讨论线索化的过程。
05
线索化
线索化的过程就是遍历中修改空指针的过程
——鲁迅(并没有说过这句话)
![fd28fa479f8130fdfbd7425242688ff9.png](https://i-blog.csdnimg.cn/blog_migrate/17bd6807152a3acc761b6002b7277d7e.png)
设前驱结点为pre,初始化为NULL,找到从根结点出发的,对应遍历顺序的第一个结点p
跟上来说就是遵循以下循环:
1.若p不存在左孩子,则把p的LTag置为1,p的左孩子设为pre;pre若不为空,且pre不存在右孩子,则把pre的RTag置为1,pre的右孩子设为p。(在pre和p之间建立线索)
2.pre设为p(表示下一个后继对应的前驱为当前的p)
3.找到p的后继结点,把后继结点设为p
因此线索化的过程和遍历过程十分相近,只要在掌握遍历算法的基础上,使用pre指针记录上一个被访问的结点,与当前访问的结点p互相建立线索就可以了。
06
算法与实现
• 首先我们要定义结点类型(是否为线索)
![0449e57916149fe5cef0f94e4b3820fe.png](https://i-blog.csdnimg.cn/blog_migrate/7dbb85b81f762b3e54f5aef7316f01f5.png)
• 然后是中序遍历线索化函数
![37bdf86c3823ff34087a7924f52d7cea.png](https://i-blog.csdnimg.cn/blog_migrate/6874d7957716d2bf7b08d9984649676d.png)
![b159efd91ecdd192de3f2cc28ed4cf98.png](https://i-blog.csdnimg.cn/blog_migrate/630708081ca2e8012af464ad408c0ea6.jpeg)
中序线索二叉树
• 对于先序遍历,先序遍历线索化函数这样写
![33005c667d6f965cd15906908f8b607c.png](https://i-blog.csdnimg.cn/blog_migrate/42752a1ac98ade8cafa8d359ea0949e9.png)
07
考查形式
好了,讲了这么多终于开始讲考题类型了!线索二叉树很少考纯编程,大部分时候出现在选择题和考察逻辑让画图的大题当中。主要考点如下。
1.要求可以画出线索树,根据遍历序列,在原有二叉树基础上,对于空指针增加虚线,使得第一个结点的左孩子指向NULL,最后一个结点的右孩子指向NULL就可以了(可以参考上文中的图)。
2.不同线索树的一些特殊性质,例如:
– 有n个结点的二叉树中有n+1个空指针,可以拥有n+1个线索
– 掌握一些临界值,例如:
中序线索树
有右孩子时,后继结点是什么,是否可达?
有左孩子时,前驱结点是什么,是否可达?
先序线索树
有右孩子时,后继结点是什么,是否可达?
有左孩子时,前驱结点是什么,是否可达?
例题:
若X是二叉中序线索树中一个有左孩子的结点,且X不为根,则X的前驱为(C)
A. X的双亲 B. X的右子树的最左结点 C. X的左子树中最右结点 B. X的左子树中最右叶节点
考察的是中序遍历的性质,LDR,对于根结点D来说,直接前驱是左子树L中的最右结点。
好啦! 我已经把搞定线索二叉树的 种种技能教给大侠你了! 希望大侠好好利用! 一定要把能够抓住的分死死抓到手里哦!今天是2020年8月20日
距离2021考研还有 120 天
行动永远比无望的焦虑更有效!
大牛学长一直在~
- END -