第12讲线索二叉树
二叉树的遍历ppt课件

后序遍历顺序:
A
B
C
DE
F
中 序遍历 : 资金是运动的价值,资金的价值是随 时间变 化而变 化的, 是时间 的函数 ,随时 间的推 移而增 值,其 增值的 这部分 资金就 是原有 资金的 时间价 值
DBEAF
C
前序遍历: A B D E C F
后序遍历
A
B
C
D EF
二叉树
资金是运动的价值,资金的价值是随 时间变 化而变 化的, 是时间 的函数 ,随时 间的推 移而增 值,其 增值的 这部分 资金就 是原有 资金的 时间价 值
总结:
我们这节课主要采用“案例驱动式”教 学方法讲解了二叉树的遍历,以案例方式讲 解通过其中两种遍历顺序推断出第三种遍历 顺序的分析方法。主要培养大家灵活运用知 识的能力和举一反三的分析能力。
一棵二叉树的中序遍历结果为 DBEAFC,前序遍历结果为ABDECF, 则后序遍历结果为 【 】 。
资金是运动的价值,资金的价值是随 时间变 化而变 化的, 是时间 的函数 ,随时 间的推 移而增 值,其 增值的 这部分 资金就 是原有 资金的 时间价 值
前序遍历:若二叉树非空,则先访问根节点,再 遍历左子树,最后遍历右子树。
资金是运动的价值,资金的价值是随 时间变 化而变 化的, 是时间 的函数 ,随时 间的推 移而增 值,其 增值的 这部分 资金就 是原有 资金的 时间价 值
拓展:
已知二叉树的 后序遍历:D A B E C 中序遍历:D E B A C
请问前序遍历结果为?
资金是运动的价值,资金的价值是随 时间变 化而变 化的, 是时间 的函数 ,随时 间的推 移而增 值,其 增值的 这部分 资金就 是原有 资金的 时间价 值
线索二叉树

6.4 线索化二叉树从前面的讨论可知,遍历二叉树就是将非线性结构的二叉树线性化,即按一定规则将二叉树中的结点排列成一个线性序列依次访问。
如图6.20(a)所示的二叉树,经中序遍历得到线性序列:BADEC,经前序遍历得到线性序列:ABCDE,经后序遍历得到线性序列:BEDCA。
在这些线性序列中,二叉树中的每个结点(除第一个和最后一个外)有且仅有唯一的一个前趋和唯一的一个后继,很容易找到各个结点的直接前驱和直接后继。
但当以二叉链表作为二叉树的存储结构时,只能找到结点的左、右孩子,而不能直接找到前驱和后继,只有在遍历的动态过程中得到这些信息。
如果将这些信息在第一次遍历时保存起来,在需要再次对二叉树进行“遍历”时就可以将二叉树视为线性结构进行访问,从而简化遍历操作。
那么,如何存储遍历中得到的结点前驱和后继的信息呢?一个简单的办法是在每个结点上增加两个指针域fwd和bkwd,分别指向存储遍历中得到的结点前驱和后继。
fwd L child data R child bkwd这是采用多重链表来表示二叉树。
这种方法虽简单易行,但这种结构的存储密度将大大降低,浪费存储空间。
另一种方法,是利用原有链域L child 和R child的空链域。
在n个结点的二叉链表中有2n个孩子链域,其中仅有n-1个链域是用来指示结点的左右孩子,而另外n+1个链域是空链域。
现在把这些空链域利用起来,使其指向结点的前驱或后继;对那些原来就不为空的链域,则仍然指向左或右孩子。
如果把指向前驱和后继的指针称为线索(Thread),那么,如何区分指向左、右孩子的指针和指向前驱、后继的线索呢?在原结点结构上增加标志域定义为:0 Lchild为左指针,指向左孩子0 Rchild为右指针,指向右孩子ltag=rtag=1 Lchild为左线索,指向前驱 1 Rchild为右线索,指向后继以这种结点构成的二叉链表作为二叉树的存储结构,叫做线索链表,其C语言类型说明如下:Typedef struct ThreadTNode{enum{0,1} ltag, rtag;Elem Type data;Struct ThreadTNode *Lchild, *Rchild;}ThreadTNode, *ThreadTree;为了节省内存空间,我们用C语言的位段方法将结点中的左标志域和右标志域与数据域合并在一个存储单元中(即各用一位表示左标志和右标志,其余各位表示结点值)。
二叉树概述

=0.xx =1; 若结点个数n=3,则有深度k=1,满足k=lb(3+1)-1=1。
二叉树概述
1.二叉树的定义
一、二叉树:是n(n≥0)个结点的有限集合。n=0的树称为空二叉树;n>0的二叉树由 一个根结点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成 。
逻辑结构: 一对二(1:2) 基本特征: ① 每个结点最多只有两棵子树(不存在度大于2的结点); ② 左子树和右子树次序不能颠倒。所以下面是两棵不同的树 注意:二叉树不是有序树
3.二叉树的性质
性质1 在一棵非空二叉树的第i层上至多有2i个结点(i≥0)。
性质2 深度为k的二叉树至多有2k+1-1个结点。 说明:深度k=-1,表示没有一个结点;深度k=0,表示只有一个根结点。
性质3 对于一棵非空的二叉树,如果叶结点个数为n0,度为2的结点数为n2, 则有 n0= n2+1。 证明:设n为二叉树的结点总数,n1为二叉树中度为1的结点个数,则有: n = n0 + n1 + n2
A
B
C
D
E
F
G
H I J K L MN O
A
B
C
D
E
F
G
H IJ
(a)满二叉树
(b)完全二叉树
问题:一个高度为h的完全二叉树最多有多少个结点?最少有多少个结点?
二叉树前驱后继的查找

线索二叉树的运算1.查找某结点*p在指定次序下的前趋和后继结点(1)在中序线索二叉树中,查找结点*p的中序后继结点在中序线索二叉树中,查找结点*p的中序后继结点分两种情形:①若*p的右子树空(即p->rtag为Thread),则p->rchild为右线索,直接指向*p的中序后继。
【例】下图的中序线索二叉树中,结点D的中序后继是A。
②若*p的右子树非空(即p->rtag为Link),则*p的中序后继必是其右子树中第一个中序遍历到的结点。
也就是从*p的右孩子开始,沿该孩子的左链往下查找,直至找到一个没有左孩子的结点为止,该结点是*p的右子树中"最左下"的结点,即*P的中序后继结点。
【例】上图的中序线索二叉树中:A的中序后继是F,它有右孩子;F的中序后继是H,它无右孩子;B的中序后继是D,它是B的右孩子。
在中序线索二叉树中求中序后继结点的过程可【参见动画演示】,具体算法如下:BinThrNode *InorderSuccessor(BinThrNode *p){//在中序线索树中找结点*p的中序后继,设p非空BinThrNode *q;if (p->rtag==Thread) //*p的右子树为空Return p->rchild;//返回右线索所指的中序后继else{q=p->rchild;//从*p的右孩子开始查找while (q->ltag==Link)q=q->lchild;//左子树非空时,沿左链往下查找return q;//当q的左子树为空时,它就是最左下结点} //end if}该算法的时间复杂度不超过树的高度h,即O(h)。
(2)在中序线索二叉树中查找结点*p的中序前趋结点中序是一种对称序,故在中序线索二叉树中查找结点*p的中序前趋结点与找中序后继结点的方法完全对称。
具体情形如下:①若*p的左子树为空,则p->1child为左线索,直接指向*p的中序前趋结点;【例】上图所示的中序线索二叉树中,F结点的中序前趋结点是A②若*p的左子树非空,则从*p的左孩子出发,沿右指针链往下查找,直到找到一个没有右孩子的结点为止。
线索二叉树

0 A0 0 B1
0 C0
1 D0
1 E1
1F1
1 G1
(b) root
0
1
ห้องสมุดไป่ตู้
0 A0
0 B1
0 C0
0 A0
0 B1
0 C0
1 D0
1 E1
1F1
1 D0
1 E1
1F1
1 G1
1 G1
(c)
(d)
线索二叉树 b—中序 c—前序 d—后序
一旦建立了某种方式的线索二叉树后,用户程序就可以 像操作双向链表一样操作该线索二叉树。
if(tree->current == tree->root) tree->nextComplete = 1;
}
int EndOfNext(ThreadBiTree *tree) //判断是否已到中序线索二叉树的最后一个结点 { return tree->nextComplete; }
例8-3 编写一个程序,首先建立如图8-10(a)所示的不带头结点的二叉树, 然后中序线索化该二叉树,最后用循环结构输出该中序线索化二叉树各结 点的序列信息。
这种算法设计要求分别设计三个函数: First():定位在第一个结点位置; Next():移动到下一个结点位置; End():是否已经到最后下一个结点位置; 当然,还需要一个根据二叉树构造线索二叉树的函数。
typedef struct { ThreadBiNode *root;
ThreadBiNode *current; int nextComplete; }ThreadBiTree;
规定:当某结点的左指针为空时,令该指针指向按某种方法遍历二叉树时 得到的该结点的前驱结点;当某结点的右指针为空时,令该指针指向按某种 方法遍历二叉树时得到的该结点的后继结点。仅仅这样做会使我们不能区分 左指针指向的结点到底是左孩子结点还是前驱结点,右指针指向的结点到底 是右孩子结点还是后继结点。因此我们再在结点中增加两个线索标志位来区 分这两种情况。
数据结构(C++)-线索二叉树精品PPT课件

一、线索二叉树
(4) 中序遍历线索树 P217
a)先由根结点指针找到根结点,从根结点起沿 左指针逐结点一直向左查找,找到左线索标志 为1的结点(“最左”的结点)即为遍历中需首先 访问的结点。 b)由此结点开始,反复进行寻找后继结点的过 程,并陆续访问这些结点,直至结束。
15
中序线索二叉树的遍历算法
13
一、线索二叉树
(3) 中序线索树求前趋结点
找前趋结点相应的原则如下:
a) 如果某结点的左线索标志域为1,说明其左指针域 是线索,这个线索所指的即是该结点的前趋结点; b) 如果某结点的左线索标志为0,则其左指针域是指 向左儿子结点的指针,由此结点的左儿子结点起按右 指针域指针逐结点向右查找,一直找到右线索标志域 为1的结点,即是该结点的前趋结点。
} }
12
一、线索二叉树
(2) 中序线索树求后继结点
在中序遍历线索树过程中,按下述两条原则即可 找到后继结点:
a) 如果某结点的右线索标志域为1,说明其右指针域 是线索,这个线索所指的即是该结点的后继结点; b) 如果某结点的右线索标志为0,则其右指针域是指 向右儿子结点的指针,由此结点的右儿子结点起按左 指针域指针逐结点向左查找,一直找到左线索标志域 为1的结点,即是该结点的后继结点。
pre->rightChild = cur; pre->rightTag = THREAD_PTR; } else if (pre != NULL) { pre->rightTag = CHILD_PTR; } pre = cur;
if (cur->rightTag == CHILD_PTR) InThreadHelp(cur->rightChild, pre);
在线索二叉树中如何求先序

1在线索二叉树中如何求先序、中序的前驱、后继,为什么后续线索二叉树是不完备的?先序前驱:若左标志为1,则左链为线索,指示其前驱;否则a) 若该结点是二叉树的根,则其前驱为空;b) 若该结点是其双亲的左孩子或是其双亲的右孩子且其双亲没有左子树,则其前驱为其双亲;c) 若该结点是其双亲的右孩子且其双亲有左子树,则其前驱为其双亲的左子树中的先序遍历列出的最后一个结点。
先序后继:若右标志为1,则右链为线索,指示其后继;否则,如果有左子树则遍历左子树第一个访问的结点,为其后继;如果没有左子树则遍历右子树第一个访问的结点,为其后继;中序前驱:若左标志为1,则左链为线索,指示其前驱;否则,遍历其左子树最后访问的结点,为其前驱中序后继:若右标志为1,则右链为线索,指示其后继;否则,遍历其右子树第一个访问的结点,为其后继后续后继:a) 若该结点是二叉树的根,则其后继为空;b) 若该结点是其双亲的右孩子或是其双亲的左孩子且其双亲没有右子树,则其后继为其双亲;c) 若该结点是其双亲的左孩子且其双亲有右子树,则其后继为其双亲的右子树中的后序遍历列出的第一个结点。
求后续后继需要知道双亲结点,而二叉链表无法找到双亲,因此不完备:5如果只想得到一个序列中前k(k>=5)个最小元素的部分排序序列,可以采用哪些排序方法,最好采用哪种排序方法?1插入、快速、归并需要全体排序不合适2起泡、简单选择、堆可以。
堆完成查找总时间:4n+klogn,起泡和简单选择总时间kn,因此堆较好。
5荷兰国旗问题分析:这个问题我们可以将这个问题视为一个数组排序问题,这个数组分为前部,中部和后部三个部分,每一个元素(红白蓝分别对应0、1、2)必属于其中之一。
由于红、白、蓝三色小球数量并不一定相同,所以这个三个区域不一定是等分的,也就是说如果我们将整个区域放在[0,1]的区域里,由于三色小球之间数量的比不同(此处假设1:2:2),可能前部为[0,0.2),中部为[0.2,0.6),后部为[0.6,1]。
遍历二叉树与线索二叉树PPT

作业:P217-218
后序列:DGJHEBIFCA, 中序列:DBGEHJACIF, 求:1、画出该二叉树; 2、先序; 3、画出该二叉树对应的森林。
由此可以看出:
(1)遍历操作实际上是将非线性结构线性化的过程, 其结果为线性序列; (2)遍历操作是一个递归的过程,因此,这三种遍历 操作的算法可以用递归函数实现。 先序遍历递归算法: DLR ( BiTree T ) { if (T) //非空二叉树 { printf(“%d”,T->data); //访问根结点D DLR(T->lchild); //递归遍历左子树 DLR(T->rchild); //递归遍历右子树 } return(0); }
这就是线索二叉树(Threaded Binary Tree)
如何预存这类信息?有两种解决方法: 缺点:空间效 ① 每个结点增加两个域:fwd和bwd; 率太低! fwd lchild data rchild bwd ② 与原有的左右孩子指针域“复用”,充分利用那n+1 个空链域。 lchild data rchild 如何判断是孩 子指针还是线 规 定: 索指针? 1)若结点有左子树,则lchild指向其左 孩子;否则,lchild指向其直接前驱(即 线索); 如何区 别? 2)若结点有右子树,则rchild指向其右 孩子;否则,rchild指向其直接后继(即线索) 。
中序遍历递归算法: LDR(BiTree T) { if(T) { LDR(T->lchild); printf(“%d”,T->data); LDR(T->rchild); } return(0); }
后序遍历递归算法 LRD (BiTree T) { if(T) { LRD(T->lchild); LRD(T->rchild); printf(“%d”,T->data); } return(0);}
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
A
B
E
先序序列 ABCDEFGHK
C
F
中序序列
D
G
HK
BDCAHGKFE 后序序列
DCBHKGFEA
2
指向该线性序列中的“前驱”和 “后继” 的指针,称作“线索”
ABCDEFGHK
^B
E^
C^
^D^
包含 “线索” 的存储结构, 称作 “线索链表”
与其相应的二叉树, 称作 “线索二叉树”
3
一、线索二叉树
一、线索二叉树 中序线索化算法
void InThreadHelp(ThreadBinTreeNode *cur, ThreadBinTreeNode *&pre)
{ if (cur != NULL) { if (cur->leftTag == CHILD_PTR) InThreadHelp(cur->leftChild, pre);
if(cur->leftChild == NULL)
{
cur->leftChild = pre;
cur->leftTag = THREAD_PTR;
}
else
{
cur->leftTag = CHILD_PTR;
}
11
一、线索二叉树 中序线索化算法
if(pre != NULL && pre->rightChild == NULL) {
cur=cur->leftChild; } } } }
17
本讲小结
重点: 1、线索二叉树
难点: 1、线索二叉树的应用
18
Homework:
1、写算法求中序线索二叉树中给定值为x
的结点之后继结点,返回该后继结点的指针。 (请编程实现)
19
template <class ElemType> void InThreadBinTree<ElemType>::InOrder (
void (*Visit)(const ElemType &)) { if (r != NULL) {
ThreadBinTreeNode <class ElemType> *cur=root; while(cur->leftTag==CHILD_PTR)
} }
12
一、线索二叉树
(2) 中序线索树求后继结点
在中序遍历线索树过程中,按下述两条原则即可 找到后继结点:
a) 如果某结点的右线索标志域为1,说明其右指针域 是线索,这个线索所指的即是该结点的后继结点; b) 如果某结点的右线索标志为0,则其右指针域是指 向右儿子结点的指针,由此结点的右儿子结点起按左 指针域指针逐结点向左查找,一直找到左线索标志域 为1的结点,即是该结点的后继结点。
定义:线索、线索化、线索二叉树
把某结点原来空的左(右)指针域用于存 放指向其前趋(后继)结点的指针,也叫左( 右)线索。
对一个二叉树中的所有结点的空指针域按 照某种遍历次序加线索的过程叫作线索化,被 线索化了的二叉树称作线索二叉树。
4
一、线索二叉树
增加两个标志域:
leftChild leftTag data rightTag rchildChild
(1) 中序线索化
对一个二叉树进行中序线索化的算法基本思想是: 一边中序遍历一边建立线索。
a) 若访问结点的左孩子结点为空,则建立前趋线索; b) 若右孩子结点为空,则建立后继线索。
为此附设一个指针pre始终指向刚刚访问过的结 点,而用指针cur指示当前正在访问的结点。pre 的初始值为NULL。
10
leftTag =
0 leftChild域指示结点的左孩子 1 leftChild域指示结点的前驱
rightTag = 0 rightChild域指示结点的右孩子 1 rightChild域指示结点的后继
5
一、线索二叉树
○A
中序线索二叉树:
○B
○C
○D ○E ○F ○G
○H ○I
图中的虚线箭头即为新加上的线索。 6
一、线索二叉树
前序序列 ABDCE
后序序列 DBECA
7
一、线索二叉树
带表头结点的中序线索二叉树
8
实战:
1、对于下图二叉树,画出其前序线索二叉树、 中序线索二叉树和后序线索二叉树。
ABBiblioteka CD EFG
H
2、n个结点的线索二叉树上含有线索数为() A 2n B n-1 C n+1 D n
9
一、线索二叉树
一、线索二叉树
定义:线索、线索化、线索二叉树
在一个n结点的链式存储二叉树中,有n+1个指 针域是空指针域,可以把每个空指针域用于存放分 别指向某种遍历次序的前趋和后继结点的指针。
在结点的空指针域中存放的该结点在某遍历次 序下的前趋结点和后继结点的指针叫做线索。
1
遍历二叉树的结果是, 求得结点的一个线性序列
cur=cur->leftChild;
16
中序线索二叉树的遍历算法
while(cur!=NULL) {
(*visit)(cur->data); if (cur->rightTag==THREAD_PTR)
cur=cur->rightChild; else {
cur=cur->rightChild; while(cur->leftChild==CHILD_PTR)
13
一、线索二叉树
(3) 中序线索树求前趋结点
找前趋结点相应的原则如下:
a) 如果某结点的左线索标志域为1,说明其左指针域 是线索,这个线索所指的即是该结点的前趋结点; b) 如果某结点的左线索标志为0,则其左指针域是指 向左儿子结点的指针,由此结点的左儿子结点起按右 指针域指针逐结点向右查找,一直找到右线索标志域 为1的结点,即是该结点的前趋结点。
14
一、线索二叉树
(4) 中序遍历线索树 P217
a)先由根结点指针找到根结点,从根结点起沿 左指针逐结点一直向左查找,找到左线索标志 为1的结点(“最左”的结点)即为遍历中需首先 访问的结点。 b)由此结点开始,反复进行寻找后继结点的过 程,并陆续访问这些结点,直至结束。
15
中序线索二叉树的遍历算法
pre->rightChild = cur; pre->rightTag = THREAD_PTR; } else if (pre != NULL) { pre->rightTag = CHILD_PTR; } pre = cur;
if (cur->rightTag == CHILD_PTR) InThreadHelp(cur->rightChild, pre);