数据结构教学 树.pptx
《数据结构教学 树.pptx》由会员分享,可在线阅读,更多相关《数据结构教学 树.pptx(124页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 图6.1是一棵由 11 个结点组成的树T。其中A是根结点,其余结点分为三个互不相交的子集:T1=B,E,F,T2=C,G,T3=D,H,I,J,K。T1、T2、T3都是树根的子树,这三棵子树的根结点分别是B、C、D。每棵子树本身也是一棵树,可继续划分。例如子树T3以D为根结点,它的其余结点又可分为三个互不相交的子集:T31=H,T32=I,K,T33=J,而其中T31、T33都可认为是仅有一个根结点的子树。第1页/共124页第2页/共124页 6.1.2 树的常用术语树结构常常用到一些术语,如度、双亲、孩子、树深等。结点的度是结点的子树的个数。树的度是树中结点度的最大值。度为零的结点称为叶子
2、或终结点。度不为零的结点称为非终端结点。图6.1中树T的度为。结点E,F,G,H,K,J度为零,它们是叶子结点。某结点的各子树的根结点称为该结点的孩子,而该结点又称为孩子们的双亲结点。具有相同双亲的结点互称为兄弟。图6.1中根结点A有三棵子树,这三棵子树的根结点分别是B,C,D,因此说结点A是结点B,C,D的双亲,而结点B,C,D又都是结点A的孩子,B,C,D之间互为兄弟。第3页/共124页 一棵树上除根结点以外的其它结点称为根结点的子孙。对于树中某结点,从根结点开始到该结点的双亲是该结点的祖先。结点的层次值从根算起,根的层次值为,其余结点的层次值为双亲结点层次值加。一棵树的深度是该树中结点最
3、大层次值,例如图6.1中的树T的深度为。结点A、B、E、K的层次值分别为1、2、3、4。第4页/共124页 6.1.3 树的表示方法 树的表示形式有多种,常见的三种方法是:(1)倒悬树法,如图6.1所示。(2)集合包含关系文氏图法,如图6.2(a)所示。(3)凹入法,如图6.2(b)所示。第5页/共124页第6页/共124页6.2 二叉树 6.2.1 二叉树的定义 二叉树(binary tree)是n(n=0)个结点的有限集合。它或为空树(n=0),或为非空树;对于非空树有:(1)有一个特定的称之为根的结点。(2)除根结点以外的其余结点分为两个互不相交的称之为左子树和右子树的二叉树构成。这个定
4、义是递归的。由于左、右子树也是二叉树,因此子树也可为空树。图6.3中展现了五种基本形态不同的二叉树。第7页/共124页第8页/共124页 6.2.2 二叉树的重要性质 性质1二叉树第i(i1)层上至多有2 i-1个结点。根据图6.4(a)可知,根结点在第一层上,这层结点数最多为1个,即20个;显然第二层上最多有2个结点,即21个;假设第i-1层的结点最多有2 i-2个,且每个结点最多有两个孩子,那么第i层上结点最多有 2*2 i-2=2 i-1个。性质2深度为k(k1)的二叉树至多有2 i-k个结点。由性质1可知各层结点最多数目之和为 20+21+22+2 k-1;第9页/共124页 由二进制
5、换算关系可知:20+21+22+2k-1=20,因此二叉树树中结点的最大数目为20。性质3在任意二叉树中,若叶子结点(即度为零的结点)个数为n0,度为1的结点个数为n1,度为2的结点个数为n2,那么 n0=n2+1。设n代表二叉树结点总数,那么 n=n0+n1+n2 (6.1)由于有n个结点的二叉树总边数为n-1条,于是得 n-1=0*n0+1*n1+2*n2 (6.2)将式(6.1)代入式(6.2)得 n0=n2+1 第10页/共124页 有两种特殊形态的二叉树,它们是满二叉树和完全二叉树。深度为k并且含有2k-1个结点的二叉树称为满二叉树,这种树的特点是每层上的结点数都是最大结点数,如图6
6、.4(a)所示。对满二叉树的结点可以从根结点开始自上向下,自左至右顺序编号,图6.4(a)中每个结点斜上角的数字即是该结点的编号。深度为k,含有n个结点的二叉树,当且仅当每个结点的编号与相应满二叉树结点顺序编号从1到n相对应时,则称此二叉树为完全二叉树,如图6.4(b)所示。而图6.4(c)则不是完全二叉树。第11页/共124页第12页/共124页 质4具有n个结点的完全二叉树树深为 log2n+1(其中x表示不大于x的最大整数)。性质5若对有n个结点的完全二叉树进行顺序编号(1in),那么,对于编号为i(i1)的结点:当i=1时,该结点为根,它无双亲结点;当i1时,该结点的双亲结点编号为i/
7、2;若2in,则有编号为2i的左孩子,否则没有左孩子;若2i+1n,则有编号为2i+1的右孩子,否则没有右孩子。对照图6.4(a)或图6.4(b),读者可看到由性质所描述的结点与编号的对应关系。第13页/共124页 6.2.3 二叉树的存储结构 二叉树常用的存储结构有两种,顺序存储结构(向量)和链表存储结构。(1)顺序存储结构(向量)可以作为二叉树的存储结构。这种存储结构适用于完全二叉树、满二叉树。假设用一维数组a存放图6.4(a)的满二叉树。可以发现图6.4(a)中结点的编号恰好与数组元素的下标相对应,见图.5。根据二叉树性质5,在a数组中可以方便地由某结点ai的下标i找到它们的双亲结点ai
8、/2或左、右孩子结点a2i、a2i+1。在哈夫曼树构造算法中也用到顺序存储结构。一般二叉树较少采用顺序存储结构。第14页/共124页第15页/共124页 (2)链表存储结构通常用于二叉树存储。常见的有二叉链表和三叉链表。二叉链表的每个结点都有一个数据域和两个指针域,一个指针指向左孩子,另一个指针指向右孩子。结点结构描述为:struct node int data;WB/*数据域*/struct node*lch,*rch;/*左、右指针域*/三叉链表的结点比前者多了一个指向双亲的指针域。结点结构描述为:第16页/共124页struct node3 int data;/*数据域*/struct
9、node*lch,*parent,*rch;/*par是双亲指针域*/对于图6.6(a)中二叉树T,它的二叉链表如图6.6(b),三叉链表如图6.6(c)。第17页/共124页第18页/共124页 6.2.4 二叉树二叉链表的一个生成算法 此方法主要利用二叉树的性质5。对任意二叉树,先按满二叉树对其进行编号,如图6.7(a)所示。由于此树并非完全二叉树,所以编号并不连续。算法中使用一个辅助向量s用于存放 指向树结点的指针,如si中存放编号为i的结点的指针,即为该结点的地址。此例原始数据序列如图6.7(b)所示,照此一一输入即可生成二叉链表。当结点编号i=1时,所产生的结点为根结点,同时将指向该
10、结点的指针存入s1。当结点编号i1时,产生一个新的结点之后,也要将指向该结点的指针存入si。第19页/共124页第20页/共124页 由性质5可知:j=i/2 为它的双亲结点编号。如果i为偶数,则它是双亲结点的左孩子,即让sj-lch=si;如果i为奇数,则它是双亲结点的右孩子,即让sj-rch=si。这样就将新输入的结点逐一与其双亲结点相连,生成二叉树。结点结构如前所描述,为struct node。辅助向量为 struct node*s20。二叉树生成算法如算法6.1。算法 6.1第21页/共124页 算法 6.1 stuct node *creat()printf(i,x=);scanf(
11、%d%d,&i,&x);while(i!KG-*2=0)&(x!KG-*2=0)q=(struct node*)malloc(sizeof(struct node)/*产生一个结点*/q-data=x;q-lch=NULL;q-rch=NULL;si=q;if(i=1)t=q;/*t 为部局变量,代表树根结点*/第22页/共124页else j=i/2;/*双亲结点编号*/if(i%2)=0)sj-lch=q;else sj-rch=q;printf(i,x=);scanf(%d%d,&i,&x);return(t)/*creat end */第23页/共124页6.3 遍 历 二 叉 树 前
12、已经指出,对于二叉树经常采用链表做为它的存储结构,本节及以后对二叉树的讨论将主要针对链表存储结构来进行 遍历二叉树是指以一定的次序访问二叉树中的每个结点,并且每个结点仅被访问一次。所谓访问结点是指对结点进行各种操作的简称。例如,查询结点数据域的内容,或输出它的值,或找出结点位置,或是执行对结点的其它操作。遍历二叉树的过程实质是把二叉树的结点进行线性排列的过程。假设遍历二叉树时,访问结点的操作就是输出结点数据域的值,那么遍历的结果得到一个线性序列。由于二叉树有左、右子树,所以遍历的次序不同,得到的结果就不同。第24页/共124页 令L、T、R分别代表遍历左子树、访问根结点和遍历右子树,则对一棵二
13、叉树的遍历可以有六种不同次序:LTR,LRT,TLR,TRL,RLT,RTL。然而经常用到的总是先左(L)后右(R),若再把访问根结点(T)的操作穿插其中,则只有三种不同的遍历次序:LTR、TLR 和 LRT。它们分别称作中根遍历、先根遍历和后根遍历二叉树。在介绍常用的三种遍历算法之前,首先介绍一下遍历的具体方法。例如有一棵二叉树有4个结点。为了便于理解遍历的思想,暂且为每个没有子树的结点均补充上相应的空子树,用来表示,见图6.8。设想有一条搜索路线(用虚线表示),它从根结点的左侧开始,自上而下自左至右搜索,最后由根结点右侧向上出去。不难看出,若考虑到空子树的话,恰好搜索路线途经每个有效的树结
14、点都是三次。把第一次经过就访问的结点列出,它们是A、B、C、D,这就是先根遍历的结果。第25页/共124页 那么第二次经过才访问的则是中根遍历,其结果是B、A、D、C。第三次经过才访问的是后根遍历,结果是B、D、C、A。二叉树的链表存储,其结点结构定义如下:struct node char data;/*假设数据类型char,根据需要也可为其它类型*/struct node *lch,*rch;第26页/共124页 6.3.1 先根遍历 先根遍历可以递归的描述如下:如果根不空:(1)访问根结点;(2)按先根次序遍历左子树;(3)按先根次序遍历右子树;否则返回。先根遍历的递归算法如算法6.2。第
15、27页/共124页算法 6.2 Void preorder(struct node*p)if(p!KG-*2NULL)printf(6ct”,p-data);/*访问根结点*/preorder(p-lch);/*按先根次序遍历左子树*/preorder(p-rch);/*按先根次序遍历右子树*/*preorder */现在针对图6.8中的二叉树,对递归算法的详细执行情况进行分析,如图6.9所示。第28页/共124页第29页/共124页第30页/共124页 图6.8中树深度为3,递归调用的深度为4层。这就是在遇到空的子树时,它也调用了一次preorder函数,只不过是因为子树的根为空立即返回而已
16、。还应注意对某根结点访问之后,便对其左子树进行先根遍历,随即进入下一层递归调用,当返回本层调用时,仍以本层根结点为基础对其右子树进行先根遍历。当从下一层递归调用(先根遍历右子树)再次返回本层时,接着就从本层调用返回到前一层调用。依此类推,最终返回主调程序。第31页/共124页 6.3.2 中根遍历 中根遍历递归的思路与先根遍历十分相似,只是在根不空的情况下所应执行的三条操作稍做调整即可。具体来说,也就是把第(1)条与第(2)条进行对换。中根遍历可以递归的描述如下:如果根不空:(1)按中根次序遍历左子树;(2)访问根结点;(3)按中根次序遍历右子树;否则返回。中根遍历递归算法如算法6.3。第32
17、页/共124页算法 6.3 Void inorder(struct node *p)if(p!NULL)inorder(p-lch);/*中根遍历左子树*/printf(6ct”,P-data);/*访问根结束*/inorder(p-rch);/*中根遍历右子树*/*inorder*/此递归算法的具体执行情况,请读者自己分析。第33页/共124页 中根遍历非递归算法如算法6.4。算法 6.4 Void inorderz(struct node*p)/*栈s是 struct node*s10*/qp;top0;/*栈顶指针*/bool1;JY/*bool=1为真值继续循环;bool=0为假值栈空
18、,结束循环*/do while(q!NULL)top;stopq;qq-lch;第34页/共124页if(top=0)bool0;else qstop;top-;printf(6ct”,q-data);/*访问根结点*/qq-rch;while(bool);/*inorderz */第35页/共124页 对照图6.8中的二叉树,在中根非递归遍历过程中其栈s的内容变化如图6.10所示。假设二叉树有个结点,对每个结点都要进行一次入栈和出栈操作,即入栈和出栈各执行n次,对结点的访问也是n次,此算法的时间复杂度为O(n)。所需栈的空间最多等于二叉树深k乘以每个结点所需空间数,可以记作O(k)。如果要写
19、先根遍历非递归算法,在搞清中根遍历非递归方法后,不难看出只要将访问语句移一位置便可实现。第36页/共124页第37页/共124页 6.3.3 后根遍历 在搞清楚先根遍历、中根遍历递归方法之后,可以看出,只要将访问根结点的语句移至遍历左子树、右子树的语言后面即可得出后根遍历递归算法。后根遍历递归算法的描如下:如果根不空:(1)按后根次序遍历左子树;(2)按后根次序遍历右子树;(3)访问根结点;否则返回。后根遍历递归算法如算法6.5。第38页/共124页算法 6.5 Void postorder(stuct node*p)if(p!KG-*2NULL)postorder(p-lch);/*后根遍历
20、左子树*/postorder(p-rch);/*后根遍历右子树*/printf(6ct”,p-data);/*访问根结点*/*postorder */第39页/共124页 后根遍历的非递归算法较为复杂,不仅在搜索线第一次经过根结点时不访问并且进栈,而且在后根遍历它的左子树之后,搜索线第二次经根结点时也不能访问。因此,在搜索线第二次经根结点时不能让栈顶元素(根)出栈,而是依据栈顶元素所表示的根去后根遍历它的右子树;直到搜索线第三次经过这个根结点时,才将其出栈,并且访问这个根结点。因此,另需一个辅助栈2用来记录经过某根结点的次数。两个栈的类型为:struct node *s10;int s220。
21、后根遍历的非递归算法如算法6.6。第40页/共124页算法 6.6 Void postorderz(stuct node*p)qp;top0;bool1;do while(q!NULL)top+;s top q;s2 top 1;qq-lch;if(top=0)bool0;else if(s2top=1)s2top2;/*第二次经过,不退栈*/第41页/共124页 qstop;qq-rch;else qstop;/*第三次经过,退栈并且访问根结点*/s2top0;top-;printf(6ct”,q-data);qNULL;while(bool);/*postorder2*/第42页/共124
22、页 算法6.7 viod levelorder(struct node*p)struct node*q20;/*辅助队列,假设树结点不超过19个*/front=0;rear=0;/*置空队列 */if(p!KG-*2=NULL)rear+;qrear=p;/*根结点进队 */while(front!KG-*2=rear)/*队首结点出队并且访问 */front+;p=qfront;printf(6ct”,t-data);if(p-lch!-=NULL)rear+;qrear=p-lch;/*P左孩子不空则进队*/第43页/共124页 if(p-rch!KG-*2=NULL)rear+;qrea
23、r=p-rch;/*P右孩子不空则进队*/*当队列不空时,继续循环 */*levelorder end */第44页/共124页 6.3.4 二叉树遍历算法的应用 1.统计二叉树中结点个数m和 叶子结点个数n 分析:在调用遍历算法时设两个计数器变量m、n。我们知道所谓遍历二叉树,即以某种次序去访问二叉树的每个结点,且每个结点仅访问一次,这就提供了方便的条件。每当访问一个结点时,在原访问语句printf后边,加上一计数器语句m+和一个判断该结点是否为叶子的语句,便可解决问题。在这里,所谓访问结点的操作拓宽为一组语句,见下列算法中的第4、5、6行。假设用中根遍历方法统计叶子结点的个数,算法如算法6
24、.8。第45页/共124页 算法 6.8 Void injishu(struct node*t)if(t!KG-*2=NULL)injishu(t-lch);/*中根遍历左子树*/printf(“6ct”,t-data);/*访问根结点*/m+;/*结点计数*/if(t-lch=NULL)&(t-rch=NULL)n+;/*叶子结点计数*/injishu(t-rch);/*中根遍历右子树*/*injishu */第46页/共124页 该函数中的printf(“6ct”,t-data)语句输出格式是针对图6.8设计的。如果数据域类型不是字符型而是整型,语句应该为printf(“6dt”,t-da
25、ta)。假设数据域类型更为复杂,就应结合具体实际重新设计输出模块。上面函数中m、n是全局变量,在主程序先置零,在调用injishu函数结束后,m值便是结点总个数,n值便是叶子结点的个数。主函数示意 如下:main()t=creat();/*建立二叉树t,为全局变量*/m=0,n=0;/*全局变量m,n置初值 */injishu(t);/*求树t中结点总数m,叶子结点的个数n*/printf(m=%4d,n=%4d,m,n);/*输出结果*/第47页/共124页 2.求二叉树的树深 首先看如下算法。算法6.9 Void predeep(struct node*t,int i)if(t!NULL)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构教学 数据结构 教学
限制150内