遍历二叉树的非递归算法Word文档下载推荐.docx
- 文档编号:19839945
- 上传时间:2023-01-10
- 格式:DOCX
- 页数:34
- 大小:103.59KB
遍历二叉树的非递归算法Word文档下载推荐.docx
《遍历二叉树的非递归算法Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《遍历二叉树的非递归算法Word文档下载推荐.docx(34页珍藏版)》请在冰豆网上搜索。
引入一个根结点
3、树的抽象数据类型定义
树特有的操作:
查找:
双亲、最左的孩子、右兄弟
结点的度不定,给出这两种操作可以查找到一个结点的全部孩子
插入、删除:
孩子
遍历:
存在一对多的关系,给出一种有规律的方法遍历(有且仅访问一次)树中的结点
ADTTree{
数据对象:
D={ai|ai∈ElemSet,i=1,2,…,n,n≥0}
数据关系:
若D为空集,则称为空树;
若D仅含一个数据元素,则R为空集,否则R={H},H是如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root}≠Ф,则存在D-{root}的一个划分D1,D2,…,Dm(m>
0)(Di表示构成第i棵子树的结点集),对任意j≠k(1≤j,k≤m)有Dj∩Dk=Ф,且对任意的i(1≤i≤m),唯一存在数据元素xi∈Di,有<
root,xi>
∈H(H表示结点之间的父子关系);
(3)对应于D-{root}的划分,H-{<
root,x1>
…,<
root,xm>
}有唯一的一个划分H1,H2,…,Hm(m>
0)(Hi表示第i棵子树中的父子关系),对任意j≠k(1≤j,k≤m)有Hj∩Hk=Ф,且对任意i(1≤i≤m),Hi是Di上的二元关系,(Di,{Hi})是一棵符合本定义的树,称为根root的子树。
基本操作:
InitTree(&
T)
操作结果:
构造空树T
DestroyTree(&
初始条件:
树T已存在
销毁树T
ClearTree(&
将树T清为空树
TreeEmpty(T)
若T为空树,则返回TRUE,否则返回FALSE
TreeDepth(T)
返回树T的深度
Root(T)
返回T的根
Value(T,cur_e)
树T已存在,cur_e是T中某个结点
返回cur_e的值
Assign(T,&
cur_e,value)
结点cur_e赋值为value
Parent(T,cur_e)
若cur_e是T的非根结点,则返回它的双亲,否则函数值为“空”
LeftChild(T,cur_e)
若cur_e是T的非叶子结点,则返回它的最左孩子,否则返回“空”
RightSibling(T,cur_e)
若cur_e有右兄弟,则返回它的右兄弟,否则返回“空”
InsertChild(&
T,p,i,c)
树T已存在,p指向T中某个结点,1≤i≤p所指结点的度+1,非空树c与T不相交
插入c为T中p所指结点的第i棵子树。
DeleteChild(&
T,p,i)
树T已存在,p指向T中某个结点,1≤i≤p所指结点的度
删除T中p所指结点的第i棵子树。
TraverseTree(T,visit())
树T已存在,visit是对结点操作的应用函数
按某种次序对T的每个结点调用函数visit()一次且至多一次。
一旦visit()失败,则操作失败
}ADTTree
6.2二叉树
一般树的度不定,直接考虑其操作比较困难,故首先考虑度为二的树。
这里引入二叉树。
6.2.1二叉树的定义
1、二叉树的特殊性
·
0≤度≤2
子树有左右之分(子树的个数=1或2时)
注意:
0≤度≤2的有序树≠二叉树
当某个结点只有一棵子树时,不存在序的概念
2、二叉树的抽象数据类型定义
二叉树相对树的特殊性:
最左的孩子、右兄弟左孩子、右孩子
遍历的规律性:
L(左子树)、D(根)、R(右子树)的排列上
限定为L在R前访问(有对称关系的,只须考虑其中的一种)
ADTBinaryTree{
(2)若D-{root}≠Ф,则存在D-{root}的一个划分DL,DR(左、右子树的结点集),且DL∩DR=Ф;
(3)若DL≠Ф,则DL中存在唯一元素xL,有<
root,xL>
∈H(H表示结点之间的父子关系),且存在DL上的关系HL
H;
若DR≠Ф,则DR中存在唯一元素xR,有<
root,xR>
∈H,且存在DR上的关系HR
(3)(DL,{HL})是一棵符合本定义的二叉树,称为根的左子树;
(DR,{HR})是一棵符合本定义的二叉树,称为根的右子树。
InitBiTree(&
构造空二叉树T
DestroyBiTree(&
二叉树T已存在
销毁二叉树T
ClearBiTree(&
将二叉树T清为空树
BiTreeEmpty(T)
若T为空二叉树,则返回TRUE,否则返回FALSE
BiTreeDepth(T)
返回二叉树T的深度
二叉树T已存在,cur_e是T中某个结点
若cur_e是T的非叶子结点,则返回它的左孩子,否则返回“空”
RightChild(T,cur_e)
若cur_e有右孩子,则返回它的右孩子,否则返回“空”
LeftSibling(T,cur_e)
返回cur_e的左兄弟,若cur_e是T的左孩子或无左兄弟,则返回“空”
返回cur_e的右兄弟,若cur_e是T的右孩子或无右兄弟,则返回“空”
T,p,LR,c)
二叉树T已存在,p指向T中某个结点,LR为0或1,非空二叉树c与T不相交
插入c为T中p所指结点的左或右子树。
p所指结点的原有左或右子树则成为c的右子树
T,p,LR)
二叉树T已存在,p指向T中某个结点,LR为0或1
根据LR为0或1,删除T中p所指结点的左或右子树。
PreOrderTraverse(T,visit())
二叉树T已存在,visit是对结点操作的应用函数
先序遍历T,对每个结点调用函数visit()一次且仅一次。
一旦visit()失败,则操作失败
InOrderTraverse(T,visit())
中序遍历T,对每个结点调用函数visit()一次且仅一次。
PostOrderTraverse(T,visit())
后序遍历T,对每个结点调用函数visit()一次且仅一次。
LevelOrderTraverse(T,visit())
层序遍历T,对每个结点调用函数visit()一次且仅一次。
}ADTBinaryTree
6.2.2二叉树的性质
1、性质1:
第i层至多有2i-1个结点(由每个结点最多只有2个孩子推出)
2、性质2:
深度为k的二叉树至多有2k-1个结点(由性质1,将各层最多的结点数累加,再结合等比数列的求和得出)
思考:
深度为k的二叉树至少有多少个结点?
(k个)深度为k的b叉树至多/至少有多少个结点?
((bk-1)/(b-1),k)
3、性质3:
n0=n2+1(ni表示二叉树中度为i的结点个数)
从两个角度考虑:
二叉树中结点的构成(根据度)n=n0+n1+n2
二叉树中充当其余结点的孩子的结点数n-1(去掉根)=n1+2×
n2
满二叉树:
达到性质1,2中所述的最大值情况
完全二叉树:
去掉最下一层的结点,其余结点形成一棵满二叉树;
叶子集中在最下2层(或1层),最下一层的结点总是尽可能地占满左边的位置
4、性质4:
具有n个结点的完全二叉树的深度为
5、性质5:
结点间的编号关系
考虑二叉树的顺序映像问题,寻求一种将二叉树映像为向量的方法:
对完全二叉树从上至下,从左至右,从根开始依次编号(1..n)。
孩子编号
双亲编号
求双亲
i
i/2(>
0)
求孩子
左:
2*i(<
n+1),右:
2*i+1(<
n+1)
右孩子编号
左孩子编号
求左兄弟
i(奇数,i>
1)
i-1(>
求右兄弟
i+1(<
i(偶数,i<
n)
满k叉树中结点间的编号关系?
p
(k≥2)
求第i个孩子
p·
k+i-(k-1)(<
结点
结点的左兄弟
p((p-1)%k≠1)
p-1(>
p+1(<
p((p-1)%k≠0)
6.2.3二叉树的存储结构
1、二叉树的顺序存储结构
1)方法
二叉树→补虚结点形成完全二叉树→自上而下、自左至右存储
2)类型定义
#defineMAX_TREE_SIZE100/*二叉树的最大结点数*/
typedefElemTypeSqBiTree[MAX_TREE_SIZE];
/*0号单元存储根结点*/
必须引入特殊符号表示虚结点的值
上述类型定义的缺陷:
未指明实际二叉树占用的长度,可改进为:
typedefstruct{
ElemTypeelem[MAX_TREE_SIZE+1];
/*1号单元存储根结点*/
intlength;
}SqBiTree;
3)不足:
空间的利用率不高
如:
若深度为5且仅含有5个结点的二叉树,必须要占用24~25-1空间。
rchild
lchild
parent
2、二叉树的链式存储结构
1)引入辅助空间表示结点间的相对关系
双亲
(1)——孩子
(2)(如右图)
data
二叉链表三叉链表
若需要找指定结点的双亲,则用三叉链表可在O
(1)时间内获得某结点的双亲;
而用二叉链表则需从根开始,采用一定的巡查方法进行搜索。
2)二叉链表的类型定义
typedefstructBiTNode{
ElemTypedata;
structBiTNode*lchild,*rchild;
/*左右孩子指针*/
}BiTNode,*BiTree;
3)二叉链表的链域
若有n个结点,则共有2n个链域;
其中n-1不为空,指向孩子。
4)二叉树(链式存储)的创建
输入序列与二叉树的映射关系
(1)完全二叉树的顺序映射
通过补虚结点,将一般的二叉树转变成完全二叉树,再对该完全二叉树的结点按自上而下、自左至右进行输入。
(2)二叉树的先序遍历
通过补虚结点,使二叉树中各实际结点均具有左右孩子,再对该二叉树按先序遍历进行输入。
(3)二叉树的两种遍历序列:
先序+中序,后序+中序
6.3树和森林
1、树的存储结构
1)双亲表示法
针对每一结点,附设指示其双亲位置的数据域。
采用顺序表(非顺序映像)。
#defineMAX_TREE_SIZE100/*树的最大结点数*/
typedefstructPTNode{
ElemTypedata;
intparent;
}PTNode;
PTNodenodes[MAX_TREE_SIZE];
intn;
/*结点数*/
}PTree;
2)孩子表示法
各结点的孩子数是不定的,用顺序表表示必须给出树的度的最大值,以及每一结点的实际度数,空间浪费大。
故以链表存储每一结点的所有孩子的位置信息。
typedefstructCTNode{/*孩子结点*/
intchild;
/*孩子结点的位置编号*/
structCTNode*next;
/*下一个孩子结点*/
}*ChildPtr;
TElemTypedata;
ChildPtrfirstchild;
/*孩子链表的头指针*/
}CTBox;
CTBoxnodes[MAX_TREE_SIZE];
intn,r;
/*结点数和根的位置*/
}CTree;
3)孩子兄弟法
二叉链表表示。
针对每一结点,引入其第一个孩子和下一个右兄弟的位置域。
typedefstructCSNode{
ElemTypedata;
structCSNode*firstchild,*nextsibling;
/*第一个孩子、下一个兄弟指针*/
}CSNode,*CSTree;
2、森林与二叉树的转换
森林用孩子兄弟法表示,形成二叉链表,可以将它理解为一个二叉树的二叉链表;
二叉树用二叉链表表示,可以将该二叉链表理解为孩子兄弟链表,从而获得森林。
6.4二叉树的先|中|后序遍历算法
1、遍历
对于二叉树中的结点,有且仅访问一次
限定L在R前访问(有对称关系的,只须考虑其中的一种)
先(根)序遍历DLR
中(根)序遍历LDR
后(根)序遍历LRD
2、二叉树遍历的递归实现
二叉树的递归定义性质,决定了它的很多算法都可用递归实现,遍历就是其中之一。
对于二叉树的遍历,可以不去具体考虑各子问题(左子树、根、右子树)的遍历结果是什么,而去考虑如何由各子问题的求解结果构成原问题(二叉树)的遍历结果——递归规律的确定。
必须注意的是,当二叉树小到一定程度,即空树时,应直接给出解答——递归结束条件及处理。
三种遍历的区别(右图):
所经过的搜索路线是相同的;
只是访问结点的时机不同。
每一结点在整个搜索路线中会经过3次(第一次进入到该结点、由左子树回溯到该结点、由右子树回溯到该结点),如在第一次遇到时就访问该结点,那么称之为先序;
第二次经过时访问为中序;
第三次经过时访问则为后序。
1)先序遍历
StatusPreOrderTraverse(BiTreeT,Status(*Visit)(ElemTypee)){
if(T!
=NULL){
if(Visit(T->
data))
if(PreOrderTraverse(T->
lchild,Visit))
rchild,Visit))
returnOK;
returnERROR;
}
elsereturnOK;
}
2)后序遍历
StatusPostOrderTraverse(BiTreeT,Status(*Visit)(ElemTypee)){
if(PostOrderTraverse(T->
3)中序遍历
StatusInOrderTraverse(BiTreeT,Status(*Visit)(ElemTypee)){
if(InOrderTraverse(T->
6.5先|后|中序遍历的应用扩展
利用二叉树的遍历算法,适当修改访问结点操作的内容,可以得到求解许多问题的算法。
✧二叉树的创建(基于先序遍历)
✧二叉树的线索化
✧查找指定结点:
在访问结点时,判断当前访问的结点是否是指定的结点,若是则返回该结点位置,否则继续遍历;
✧查找位于某种遍历次序中的第k位的结点:
遍历前,引入一计数器,用来统计已访问过的结点数,初值为0;
在访问结点时,将该计数器增1,并看是否达到k,……;
✧求叶子结点的数目:
遍历前,引入叶子结点计数器,初值为0;
访问结点时,将该计数器增1;
遍历结束,则计数器中的值即为所求解;
✧判断二叉树中是否仅包含有度为2和0的结点:
访问结点时,判断该结点孩子的有无情况,……
✧求指定结点的层次:
孩子结点的层次比其双亲结点层次多一;
✧求二叉树的高度:
二叉树的高度=max(左子树的高度,右子树的高度)+1
6.5.1基于先序遍历的二叉树(二叉链)的创建
【本例特征】
如何基于二叉树的先序、中序、后序遍历的递归算法进行问题求解?
【思路】
先序遍历PreOrderTraverse
创建CreateBiTree
输入
二叉链表示的二叉树的头指针T
带虚结点的先序序列ch
输入的表现方式
参数
由输入设备输入scanf(&
ch)
输出
对结点的访问结果
输出的表现方式
由输出设备输出
变参
空树(递归结束)的条件及处理
(直接求解)
T==NULL
ch==END_DATA(表示虚结点的值)
空
T=NULL
根结点的访问
(子问题直接求解)
Visit(T->
data)
T=(BiTree)malloc(sizeof(BiTNode))
T->
data=ch
左子树
(使用递归调用的解)
PreOrderTraverse(T->
lchild)
CreateBiTree(T->
右子树
rchild)
【算法】见《数据结构(C语言版)》P131算法6.4。
6.5.2统计二叉树中叶子结点的数目
如何通过全局变量、变参、返回值三种渠道返回处理结果?
在遍历二叉树时,对一些特殊的结点(无左右孩子)进行计数。
可以修改遍历算法的结点访问操作为对特殊结点的判定和计数过程,需要注意的是计数器的处理方式。
可以有以下几种计数处理:
用遍历函数的返回值传出求得的叶子结点的数目;
为遍历函数增加一个引用参数,用来传出指定二叉树的叶子结点数目。
引入全局的计数器,初始为0;
此处,遍历次序的选择对本算法没有太大影响。
【算法1全局的计数器】
//n为叶子结点的计数器
intn=0;
voidleaf(BiTreeT)
{//利用二叉树的先序遍历
if(T!
//访问结点->
叶子的判定和计数
if(T->
lchild==NULL&
&
T->
rchild==NULL)n++;
leaf(T->
lchild);
rchild);
}//调用结束,即可由n获得二叉树T的叶子结点数目,需注意下次调用前须n=0;
【算法2以函数返回值返回】
//函数值为T的叶子结点数
intleaf(BiTreeT)
{//利用二叉树的中序遍历,n为局部变量
n
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 遍历 二叉 递归 算法