第6章--树与二叉树-数据结构课件.ppt
《第6章--树与二叉树-数据结构课件.ppt》由会员分享,可在线阅读,更多相关《第6章--树与二叉树-数据结构课件.ppt(157页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第6章 树与二叉树 第6章 树与二叉树 6.1 树的基本概念和术语树的基本概念和术语 6.2 二叉树二叉树 6.3 遍历二叉树遍历二叉树 6.4 线索二叉树线索二叉树 6.5 二叉树、树和森林二叉树、树和森林 6.6 树的应用树的应用 6.7 有关二叉树的有关二叉树的C语言源程序语言源程序 第6章 树与二叉树 6.1 树的基本概念和术语树的基本概念和术语 6.1.1 树的定义树的定义树(tree)是由一个或多个结点组成的有限集合T。其中:(1)一个特定的结点称为该树的根(root)结点;(2)结点之外的其余结点可分为m(m0)个互不相交的有限集合T1,T2,Tm,且其中每一个集合本身又是一棵树
2、,称之为根的子树(subtree)。这是一个递归的定义,即在定义中又用到了树这个术语。它反应了树的固有特性。可以认为仅有一个根结点的树是最小树,树中结点较多时,每个结点都是某一棵子树的根。第6章 树与二叉树 在图6.1中是一棵由11个结点组成树T。其中A是根结点,其余结点分为三个互不相交的子集:T1=B,E,F,T2=C,G,T3=D,H,I,J,K。T1,T2,T3都是树根A的子树,这三棵子树的根结点分别是B,C,D。每棵子树本身也是一棵树,可继续划分。例如子树T3以D为根结点,它的其余结点又可分为三个互相不交的子集T31=H,T32=I,K,T33=J,而其中T31,T33可都认为是仅有一
3、个根结点的子树。第6章 树与二叉树 图6.1树T第6章 树与二叉树 第6章 树与二叉树 图6.1中根结点A有三棵子树,这三棵子树的根结点分别是B,C,D,因此说结点A是结点B,C,D的双亲,而结点B,C,D又都是结点A的孩子,B,C,D之间互为兄弟。一棵树上除根结点以外的其他结点称为根结点的子孙。对于树中某结点,从根结点开始到该结点的双亲是该结点的祖先。结点的层次值从根算起,根的层次值为1,其余结点的层次值为双亲结点层次值加1。一棵树的深度是该树中结点最大层次值,例如图6.1中的树T的深度为4。结点A,B,E,K的层次值分别为1,2,3,4。第6章 树与二叉树 6.1.3 树的表示形式树的表示
4、形式树的表示形式有多种,常见的三种形式是:(1)倒悬树法,如图6.1所示;(2)集合包含关系文氏图法,如图6.2(a)所示;(3)凹入法,如图6.2(b)所示。第6章 树与二叉树 图6.2树结构的不同表示方法(a)文氏图法;(b)凹入法第6章 树与二叉树 第6章 树与二叉树 图6.3二叉树的五种基本形态其中(a)为空树,(b)为仅有一个结点的二叉树,(c)是仅有左子树而右子树为空的二叉树,(d)是仅有右子树而左子树为空的二叉树,(e)是左、右子树均非空的二叉树。这里应特别注意的是,二叉树的左子树和右子树是严格区分并且不能随意颠倒的,图6.3(c)与图6.3(d)就是两棵不同的二叉树。第6章 树
5、与二叉树 6.2.2 二叉树的重要性质二叉树的重要性质性质1 二叉树第i(i1)层上至多有2i-1个结点。证明:根据图6.4(a)可知,根结点在二叉树的第一层上,这层结点数最多为1个,即20个;显然第二层上最多有2个结点,即21个;假设第i-1层的结点最多有2i-2个,且每个结点最多有两个孩子,那么第i层上结点最多有2*2i-2=2i-1个。性质1证明完毕。第6章 树与二叉树 第6章 树与二叉树 性质3 在任意二叉树数中,若叶子结点(即度为零的结点)个数为n0,度为1的结点个数为n1,度为2的结点个数为n2,那么n0=n2+1。证明证明:设n代表二叉树结点的总数,那么n=n0+n1+n2(6.
6、1)由于有n个结点的二叉树总边数为n-1条,于是得n-1=0*n0+1*n1+2*n2(6.2)将(6.1)式代入(6.2)式得n0=n2+1性质3证明完毕。第6章 树与二叉树 第6章 树与二叉树 图6.4满二叉树和完全二叉树(a)满二叉树;(b)完全二叉树;(c)非完全二叉树第6章 树与二叉树 性质4 具有n个结点的完全二叉树树深为log2n+1(其中|x|表示不大于x的最大整数)。证明:假设某完全二叉树的结点总数是n,它的值应该大于树深为k-1的满二叉树结点数2k-1-1,小于等于树深为k的满二叉树结点数2k-1。2k-1-1n2k-1由于该不等式各项均为整数,当对两端两项各加1时不等式发
7、生变化得:2k-1n2k再对其取对数得:k-1lbn1时,产生一个新的结点之后,也要将指向该结点的指针存入si。由性质5可知:j=i/2为它的双亲结点编号。第6章 树与二叉树 第6章 树与二叉树 算法 6.1 Bnode*creat()Bnode*t=NULL;printf(ni,x=);scanf(%d%d,&i,&x);while(i!=0)&(x!=0)q=(structnode*)malloc(sizeof(structnode);/*产生一个结点*/q-data=x;q-lch=NULL;q-rch=NULL;si=q;if(i=1)t=q;/*q为树根结点*/elsej=i/2;/
8、*j为双亲结点编号*/第6章 树与二叉树 if(i%2)=0)sj-lch=q;elsesj-rch=q;printf(ni,x=);scanf(%d%d,&i,&x);returut;/*creatend*/第6章 树与二叉树 第6章 树与二叉树 6.3 遍历二叉树遍历二叉树 遍历二叉树是指以一定的次序访问二叉树中的每个结点,并且每个结点仅被访问一次。所谓访问结点是指对结点进行各种操作的简称。例如,查询结点数据域的内容,或输出它的值,或找出结点位置,或是执行对结点的其他操作。遍历二叉树的过程实质是把二叉树的结点进行线性排列的过程。假设遍历二叉树时访问结点的操作就是输出结点数据域的值,那么遍历
9、的结果得到一个线性序列。由于二叉树有左、右子树,因此遍历的次序不同,得到的结果就不同。令L代表遍历左子树,T代表访问根结点,R代表遍历右子树,则对一棵二叉树的遍历可以有六种不同次序:LTR,LRT,TLR,TRL,RLT,RTL。然而经常用到的总是先左(L)后右(R),若再把访问根结点(T)的操作穿插于其中,则只有三种不同的遍历次序:LTR、TLR和LRT。它们分别称作中根遍历二叉树、先根遍历二叉树和后根遍历二叉树。第6章 树与二叉树 在介绍常用的三种遍历算法之前,首先介绍一下遍历的具体方法。例如有一棵二叉树它有4个结点。为了便于理解遍历的思想,暂且为每个没有子树的结点均补充上相应的空子树,用
10、来表示,见图6.8。设想有一条搜索路线(用虚线表示),它从根结点的左侧开始,自上而下自左至右搜索,最后由根结点的右侧向上出去。不难看出,若考虑到空子树的话,恰好搜索线途经每个有效的树结点都是三次。把搜索线第一次经过就访问的结点列出,它们是A、B、C、D,这就是先根遍历的结果。那么搜索线第二次经过才访问的则是中根遍历,其结果是B、A、D、C。搜索线第三次经过才访问的就是后根遍历,结果是B、D、C、A。第6章 树与二叉树 二叉树选择不同的存储结构,遍历算法的程序代码会有所不同。这里确定用二叉链表作为存储结构,树中结点的结构定义如下:typedefstructnodechardata;/*假设数据类
11、型是char,根据需要也可为其他类型*/structnode*lch,*rch;Bnode;二叉树结点的类型名就是Bnode(Binarynode)。第6章 树与二叉树 6.3.1 先根遍历先根遍历先根遍历可以递归的描述如下:如果根不空,则(1)访问根结点;(2)按先根次序遍历左子树;(3)按先根次序遍历右子树;否则返回。在理解和认识遍历算法时,务必分辨清楚“访问”与“遍历”是两个不同的概念,“访问”操作是针对一个结点,“遍历”操作是针对一棵树。第6章 树与二叉树 算法算法 6.2voidpreorder(Bnode*p)if(p!NULL)printf(%6c,p-data);/*访问根结点
12、*/preorder(p-lch);/*按先根次序遍历左子树*/preorder(p-rch);/*按先根次序遍历右子树*/*preorder*/第6章 树与二叉树 图图6.9 先根遍历递归调用示意图先根遍历递归调用示意图 第6章 树与二叉树 图6.8中树深度为3,递归调用的深度为4层。这就是在遇到空的子树时,它也调用了一次preorder函数,只不过是因为子树的根为空立即返回而已。还应注意对某根结点访问之后,便对其左子树进行先根遍历,随即进入下一层递归调用。当返回本层调用时,仍以本层根结点为基础对其右子树进行先根遍历。当再从下一层递归调用(即先根遍历右子树)再次返回本层时,接着就从本层调用返
13、回到前一层调用。依此类推,最终返回主调程序。第6章 树与二叉树 6.3.2 中根遍历中根遍历中根遍历递归的思路与先根遍历十分相似,只是在根不空的情况下所应执行的三条操作稍做调整即可。具体来说,也就是把第(1)条与第(2)进行条对换。中根遍历可以递归的描述如下:如果根不空,则(1)按中根次序遍历左子树;(2)访问根结点;(3)按中根次序遍历右子树;否则返回。第6章 树与二叉树 算法 6.3voidinorder(Bnode*p)if(p!NULL)inorder(p-lch);/*中遍历左子树*/printf(%6c,P-data);/*访问根结束*/inorder(p-rch);/*中根遍历右
14、子树*/*inorder*/第6章 树与二叉树 递归算法简明精练,但效率较低。在实际应用中往往会用到非递归方法。在某些高级语言中,没有提供递归调用的语句及功能。为了提高程序设计的能力,有必要进行由递归方法到非递归方法的基本训练。由前面6.3.1节中对递归方法执行的分析可看出,每进行一次深层次调用都需保留当前调用层上的一些信息,主要是指向根结点的指针。现在介绍中根遍历的非递归算法,这里需要人为地设置一个栈S来存放所经过的根结点的指针。显然,第一次遇到根结点并不访问,而是入栈,然后中根遍历它的左子树。左子树遍历结束后,第二次遇到根结点,就将根结点(指针)退栈并且访问根结点,随后中根遍历其右子树。在
15、需要退栈时,如果栈空则结束。第6章 树与二叉树 算法 6.4voidinorderz(Bnode*p)/*栈s是Bnode*s10*/qp;top0;/*栈顶指针*/boo1;/*bool=1为真值继续循环;bool=0为假值栈空,结束循环*/printf(n中根遍历:n)dowhile(q!NULL)top+;stopq;qq-lch;第6章 树与二叉树 if(top=0)bool0;elseqstop;top-;printf(%6c,q-data);/*访问根结点*/qq-rch;while(bool);printf(n)/*inorderz*/第6章 树与二叉树 图6.10中根非递归遍历
16、过程中栈S的变化第6章 树与二叉树 假设二叉树有n个结点,对每个结点都要进行一次入栈和出栈操作,即入栈和出栈各执行n次,对结点的访问也是n次。这样,此算法的时间复杂度为O(n)。所需栈的空间最多等于二叉树深k乘以每个结点所需空间数,可以记作O(k)。如果要写先根遍历非递归算法,在搞清中根遍历非递归方法后,不难看出只要将访问语句移一位置便可实现。第6章 树与二叉树 6.3.3 后根遍历后根遍历在搞清楚先根遍历,中根遍历递归方法之后,可以看出,只要将访问根结点的语句移至遍历左子树、右子树的语言后面即可得出后根遍历递归算法。后根遍历可以递归的描述如下:如果根不空,则(1)按后根次序遍历左子树;(2)
17、按后根次序遍历右子树;(3)访问根结点;否则返回。第6章 树与二叉树 算法 6.5Voidpostorder(Bnode*p)if(p!NULL)postorder(p-lch);/*后根遍历左子树*/postorder(p-rch);/*后根遍历右子树*/printf(%6c,p-data);/*访问根结点*/*postorder*/第6章 树与二叉树 后根遍历的非递归算法较为复杂,不仅在搜索线第一次经过根结点时不访问并且进栈S,而且在后根遍历它的左子树之后,搜索线第二次经根结点时也不访能问。因此,在搜索线第二次经根结点时不能让栈顶元素(根)出栈,而是依据栈顶元素所表示的根去后根遍历它的右子
18、树;直到搜索线第三次经过这个根结点时,才将其出栈,并且访问这个根结点。因此,另需一个辅助栈S2用来记录经过某根结点的次数。两个栈的类型:Bnode*s10;ints220;第6章 树与二叉树 算法 6.6Voidpostorderz(Bnode*p)qp;top0;bool1;printf(n后根遍历:n)dowhile(q!NULL)top+;stopq;s2top1;qq-lch;if(top=0)bool0;elseif(s2top=1)s2top2;/*第2次经过,不出栈*/qstop;qq-rch;第6章 树与二叉树 elseqstop;/*第3次经过,出栈并且访问结点*/s2top
19、0;top-;printf(%6c,q-data);qNULL;whilebool;printf(n);/*postorder2*/第6章 树与二叉树 此算法因使用了两个栈,它的空间占用是中根遍历非递归算法的两倍,k为树深时,空间需要量仍记作O(k)。算法中访问每个结点n次,记作O(n);伴随遍历每结点都要涉及两次入栈和两次出栈操作,也记作O(n);所以总的时间复杂度仍为O(n)。第6章 树与二叉树 6.3.4 按层遍历二叉树按层遍历二叉树算法 6.7viodlevelorder(Bnode*p)Bnode*q20;front=0;rear=0;if(p!=NULL)rear+;qrear=p
20、;/*根结点不空,进队*/while(front!=rear)front+;p=qfront;printf(%6c,p-data);/*出队并访问*/*若左孩子不空,进队*/if(p-lch!=NULL)rear+;qrear=p-lch;/*若右孩子不空,进队*/if(p-rch!=NULL)rear+;qrear=p-rch;/*levelorder*/第6章 树与二叉树 6.3.5 二叉树遍历算法的应用二叉树遍历算法的应用利用二叉树遍历算法的框架和思路,假设访问结点的具体操作不仅仅局限于输出结点数据域的值,而把访问延伸到对结点的判别、计数等其他操作。这样可以解决一些关于二叉树的其他实际问
21、题。1.统计二叉树中结点总数统计二叉树中结点总数m,叶子结点个数,叶子结点个数n0分析:在调用遍历算法时设两个计数器变量m、n0,我们知道所谓遍历二叉树,即以某种次序去访问二叉树的每个结点,且每个结点仅访问一次,这就提供了方便的条件。每当访问一个结点时,在原访问语句printf后边,加上一条计数器语句m+和一个判断该结点是否叶子的语句,便可解决问题。在这里所谓访问结点的操作拓宽为一组语句,见下列算法中的第4、5、6行。第6章 树与二叉树 假设用中根遍历方法统计叶子结点个数,算法如下:算法 6.8Voidinjishu(Bnode*t)if(t!=NULL)injishu(t-lch);/*中根
22、遍历左子树*/printf(%6c,-data);/*访问根结点*/m+;/*结点记数*/if(t-lch=NULL)&(t-rch=NULL)n0+;/*叶结点记数*/injishu(t-rch);/*中根遍历右子树*/*injishu*/第6章 树与二叉树 该函数中的m、n0是全局变量,在主程序先置零,在调用injishu函数结束后,m值便是结点总个数,n0值便是叶子结点的个数。主函数示意如下:intm,n0;main()Bnodet;t=creat();/*建立二叉树t*/m=0,n0=0;/*全局变量m,n置初值*/injishu(t);/*求树t中结点总数m,叶子结点的个数n0*/p
23、rintf(nm=%4d,n=%4d,m,n0);/*输出结果*/当然,也可用先根或后根遍历方法统计结点个数。对照图6.8可知二叉树结点总数为4,叶子结点数为2。第6章 树与二叉树 2.求二叉树的树深求二叉树的树深首先看如下算法:算法 6.9Voidpredeep(Bnode*t,inti)if(t!NULL)printf(%d,tdata);/*访问根结点*/i+;if(klch,i);/*先根遍历左子树*/predeep(t-rch,i);/*先根遍历右子树*/*predeep*/第6章 树与二叉树 由算法看出它利用了先根遍历二叉树的思路,只是在这里“访问结点”操作复杂了一些,如算法中第3
24、、4、5行。其中k为全局变量在主程序中置初值零,在调用函数predeep之后,k值就是树的深度。形参i在主程序调用时,用一个初值为零的实参代入。当深度递归调用时,i会不断增大,k记下它的值。当返回时退到哪一个调用层次,i会保持在本层次的原先较小值。而在返回时不论退到哪一个调用层次,k将保持较大值不变,这样,k就是树深。对照图6.8可知最终k=3,树深为3。相应的主函数为第6章 树与二叉树 intk;main()Bnode*t;t=creat();/*建立二叉树t*/k=0;i=0;/*k,i置初值,其中k为全局变量*/predeep(t,i);/*求树t的深度,i为一个辅助变量*/printf
25、(nk=%d,k);/*输出树深k*/第6章 树与二叉树 6.线索二叉树线索二叉树 6.4.1 线索二叉树的基本概念线索二叉树的基本概念具有n个结点的二叉树,有n-1条边,正是这些边指向其左孩子、右孩子。这意味着在二叉链表的2n个孩子指针域中只用到了n-1个域,还有另外n+1个指针域为空,被闲置。现设法将这些空闲的指针域利用起来。当某结点无左孩子时,令左指针指向它的前趋结点;当该结点无右孩子时,令右指针指向它的后继结点。为了严格区分结点的孩子指针域究竟指向孩子结点还是指向前趋或后继结点,需在原结点结构中增加两个标志域。新的结点结构为:第6章 树与二叉树 typedefnodechardata;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 二叉 数据结构 课件
限制150内