1、数据结构清华大学殷人昆2数据结构清华大学殷数据结构清华大学殷人昆人昆第第 4 章章 树与二叉树树与二叉树树和森林的概念树和森林的概念n树的定义树的定义 r树是由树是由n(n0)个结点组成的有限集合:个结点组成的有限集合:有一个特定的称之为根有一个特定的称之为根(root)的结点;的结点;除根以外的其它结点划分为除根以外的其它结点划分为 m(m0)个个 互不相交的有限集合互不相交的有限集合T1,T2,Tm,每个,每个集合又是一棵树,并且称之为根的子树。集合又是一棵树,并且称之为根的子树。n此定义是离散数学和图论中给出的。它们把树此定义是离散数学和图论中给出的。它们把树视为图的一个极小连通子图。视
2、为图的一个极小连通子图。有有 n 个结点的树个结点的树有有 n-1 条边,而且不能有回路。条边,而且不能有回路。n这种观点的典型代表是这种观点的典型代表是 Knuth。他。他认为认为图至少图至少有一个顶点,树也必须至少有一个顶点。有一个顶点,树也必须至少有一个顶点。树不树不能是空树,但能是空树,但 N 叉树可以是空树。叉树可以是空树。N 叉树是限叉树是限制根的子树最多不能超过制根的子树最多不能超过 N 棵的树。棵的树。n随着对树讨论的深入,人们放宽了对树结构的随着对树讨论的深入,人们放宽了对树结构的限制。若把树当作层次结构单独对待,树中允限制。若把树当作层次结构单独对待,树中允许结点个数许结点
3、个数为为0。这样,树与。这样,树与N叉树就没有不可叉树就没有不可逾越的鸿沟了。逾越的鸿沟了。n从面向对象从面向对象观点,观点,N叉树是树的特殊实现:树是叉树是树的特殊实现:树是父类,父类,N叉树是子类。叉树是子类。N叉树继承了树的特性,叉树继承了树的特性,它还有自己增加的特性,例如它还有自己增加的特性,例如r理论上树不可是空树,理论上树不可是空树,N叉树可以是空树;叉树可以是空树;r树的子树可以有序,也可以不要求有序,而树的子树可以有序,也可以不要求有序,而N叉树的子树必须有序。叉树的子树必须有序。rN叉树的定义也是递归的,递归到空树终止;叉树的定义也是递归的,递归到空树终止;树则递归到只有一
4、个结点的子树。树则递归到只有一个结点的子树。r树的子树棵数不限,而树的子树棵数不限,而N叉树中根的子树最叉树中根的子树最多多N棵。棵。r树可以区分为外向树和内向树,而树可以区分为外向树和内向树,而N叉树一叉树一般是外向树,即边是有向的,从父指向子。般是外向树,即边是有向的,从父指向子。r树可以用树可以用N叉树实现。二叉树、叉树实现。二叉树、B树等又都是树等又都是N叉树的特殊情形。叉树的特殊情形。树的特点树的特点n树是分层结构,又是递归结构。每棵子树的根树是分层结构,又是递归结构。每棵子树的根结点有且仅有一个直接前驱,但可以有结点有且仅有一个直接前驱,但可以有 0 个或个或多个直接后继。多个直接
5、后继。1层层2层层4层层3层层depth=4height=4ACGBDEFKLHMIJ前驱前驱后继后继1层2层4层3层depth=4height=4ACGBDEFKLHMIJ结点结点结点的度结点的度分支结点分支结点叶结点叶结点子女子女双亲双亲兄弟兄弟祖先祖先树的度树的度树树深度深度树高度树高度森林森林子孙子孙结点层次结点层次结点深度结点深度结点高度结点高度n注意,结点的深度和结点的高度是不同的。结点注意,结点的深度和结点的高度是不同的。结点的深度即结点所处层次,是从根向下逐层计算的;的深度即结点所处层次,是从根向下逐层计算的;结点的高度是从下向上逐层计算的:结点的高度是从下向上逐层计算的:叶结
6、点的高叶结点的高度为度为1,其他结点的高度是取它的所有子女结点最其他结点的高度是取它的所有子女结点最大高度加一。大高度加一。n树的深度与高度相等。树的深度与高度相等。树的深度按离根最远的树的深度按离根最远的叶结点算,树的高度按叶结点算,树的高度按根结点算,都是根结点算,都是6。l叶结点也称为终端结点,叶结点也称为终端结点,非叶结点也称为非终端非叶结点也称为非终端结点。结点。ABCDEFGHILKJ高度=4深度=3二叉树的五种不同形态二叉树的五种不同形态LLRR二叉树二叉树(Binary Tree)n二叉树的定义二叉树的定义一棵二叉树是结点的一个有限集合,该集合或者一棵二叉树是结点的一个有限集合
7、,该集合或者为空,或者是由一个根结点加上两棵分别称为左为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。子树和右子树的、互不相交的二叉树组成。n这个定义是递归的。这个定义是递归的。二叉树的性质二叉树的性质性质性质1 若二叉树的层次从若二叉树的层次从 1 开始开始,则在二叉树的第则在二叉树的第 i 层层最多有最多有 2i-1 个结点。个结点。(i1)证明用数学归纳法证明用数学归纳法r i=1时,根结点只有时,根结点只有1个,个,21-1=20=1;r若设若设 i=k 时性质成立,即该层最多有时性质成立,即该层最多有 2k-1 个个结点,则当结点,则当 i=k+1 时
8、,由于第时,由于第 k 层每个结点层每个结点最多可有最多可有 2 个子女,第个子女,第 k+1 层最多结点个数层最多结点个数可有可有 2*2k-1=2k 个,故性质成立。个,故性质成立。性质性质2高度为高度为 h 的二叉树最多有的二叉树最多有 2h-1个结点。个结点。(h1)证明用求等比级数前证明用求等比级数前k项和的公式项和的公式高度为高度为 h 的二叉树有的二叉树有 h 层,各层最多结点个数相层,各层最多结点个数相加,得到等比级数,求和得:加,得到等比级数,求和得:20+21+22+2h-1=2h-1n 空树的高度为空树的高度为 0,只有根结点,只有根结点的树的高度为的树的高度为 1。性质
9、性质3对任何一棵二叉树对任何一棵二叉树,如果其叶结点有如果其叶结点有 n0 个个,度为度为2的非叶结点有的非叶结点有 n2 个个,则有则有 n0n21证明:证明:若设度为若设度为 1 的结点有的结点有 n1 个,总结点个数为个,总结点个数为 n,总边数为总边数为 e,则根据二叉树的定义,则根据二叉树的定义,n=n0+n1+n2 e=2n2+n1=n-1因此,有因此,有 2n2+n1=n0+n1+n2-1n2=n0-1 n0=n2+1n引申:可用于判断二叉树各类结点个数。引申:可用于判断二叉树各类结点个数。4.1.2安全生产管理的基本知识、方法与安全生产,有关行业安全生4注意眼神【案例】服务是一
10、种行动,是做出来能够使顾客感受得到的东西。提升服务者的心理素质,使他们主动、积极、热忱、开放,才能为客户提供贴心服务。通过潜能激发训练可以提升服务者的心理素质,包括观念、心态和行为技术的改进和提高。3.1经评标委员会评议认定有下列情形之一的,属于投标人相互串通投标:(4)本标书第二章“前附表”中规定的其他验收方式。4.7.3岗位安全操作规程,生产设备、安全装置、劳动防护用品的性能及正确使用方法,事故案例27.4 招标代理机构将按本须知第27.2条的内容作开标记录,存档备查。10.6 在质量保证期内,如果发现由于卖方责任造成货物的质量或规格与合同不符,或证实货物是有缺陷的,包括潜在的缺陷或使用不
11、符合要求的材料等,或由于卖方技术文件错误或卖方人员在安装、调试、试运行和验收过程中错误指导而导致货物损坏,买方可以根据本合同的规定以书面形式向卖方提出补救措施或索赔。32.1 评标委员会在初审时将检查其报价是否有算术错误,对价格的算术错误按下述原则修正。修正后的结果对投标人有约束力,如投标人不接受修正后的结果,则其投标将被拒绝,投标保证金将不予退还。定义定义1 满二叉树满二叉树(Full Binary Tree)定义定义2 完全二叉树完全二叉树(Complete Binary Tree)r若设二叉树的高度为若设二叉树的高度为 h,则共有,则共有 h 层。除第层。除第 h 层外,其它各层层外,其
12、它各层(1h-1)的结点数都达到最大的结点数都达到最大个数,第个数,第 h 层从右向左连续缺若干结点,这就层从右向左连续缺若干结点,这就是完全二叉树。是完全二叉树。性质性质4具有具有 n(n0)个结点的完全二叉树的高度为个结点的完全二叉树的高度为 log2(n+1)证明:设完全二叉树的高度为证明:设完全二叉树的高度为 h,则有,则有 2h-1-1n 2h-1 上面上面h-1层结点数层结点数 包括第包括第h层的最大结点数层的最大结点数变形变形 2h-1n+12h 取对数取对数 h-1log2(n+1)h 有有 h=log2(n+1)23-124-123-124-1注意:注意:n求高度的另一公式为
13、求高度的另一公式为 log2n +1,此公式对于此公式对于 n=0 不适用。不适用。n若设完全二叉树中叶结点有若设完全二叉树中叶结点有 n0 个个,则该二叉树总则该二叉树总的结点数为的结点数为 n=2n0,或或 n=2n0 1。n若完全二叉树的结点数为奇数,没有度为若完全二叉树的结点数为奇数,没有度为1的结的结点;为偶数,有一个度为点;为偶数,有一个度为1的结点。的结点。n右图为理想平衡树,右图为理想平衡树,上层都是满的,第上层都是满的,第 h 层结点分布在各层结点分布在各处。处。性质性质5如将一棵有如将一棵有 n 个结点的完全二叉树自顶向下,同个结点的完全二叉树自顶向下,同一层自左向右连续给
14、结点编号一层自左向右连续给结点编号:0,1,2,n-1,则有以下关系:则有以下关系:r若若i=0,则则 i 无双亲;无双亲;若若i 0,则则 i 的双亲为的双亲为(i-1)/2。r若若2*i+1 n,则则 i 的左子女为的左子女为 2*i+1;若若2*i+2 1,则则 i 的双亲为的双亲为 i/2。r若若2*i=n,则则 i 的左子女为的左子女为 2*i;若若2*i+1 lchild);visit(T-data);InOrder(T-rchild);nvisit()是输出数据值的操作,在实际使用时可用相是输出数据值的操作,在实际使用时可用相应的操作来替换。应的操作来替换。前序遍历二叉树算法的框
15、架是:前序遍历二叉树算法的框架是:n若二叉树为空,则空操作;若二叉树为空,则空操作;n否则否则u访问根结点访问根结点(V);u前序遍历左子树前序遍历左子树(L);u前序遍历右子树前序遍历右子树(R)。遍历结果遍历结果-+a*b-c d/e f前序遍历前序遍历(Preorder Traversal)-/+*abcdef二叉树递归的前序遍历算法二叉树递归的前序遍历算法void PreOrder(BiTNode*T)if(T!=NULL)visit(T-data);PreOrder(T-lchild);PreOrder(T-rchild);n与中序遍历算法相比,与中序遍历算法相比,visit()操作
16、放在两个子树操作放在两个子树递归前序遍历的最前面。递归前序遍历的最前面。后序遍历二叉树算法的框架是:后序遍历二叉树算法的框架是:n若二叉树为空,则空操作;若二叉树为空,则空操作;n否则否则u后序遍历左子树后序遍历左子树(L);u后序遍历右子树后序遍历右子树(R);u访问根结点访问根结点(V)。遍历结果遍历结果 a b c d-*+e f/-后序遍历后序遍历(Postorder Traversal)-/+*abcdef二叉树递归的后序遍历算法二叉树递归的后序遍历算法void PostOrder(BiTNode*T)if(T!=NULL)PostOrder(T-lchild);PostOrder(
17、T-rchild);visit(T-data);n与中序遍历算法相比,与中序遍历算法相比,visit()操作放在两个子树操作放在两个子树递归前序遍历的最后面。递归前序遍历的最后面。计算二叉树结点个数的递归算法计算二叉树结点个数的递归算法应用二叉树遍历的事例应用二叉树遍历的事例int Count(BiTNode*T)if(T=NULL)return 0;else return 1+Count(T-lchild)+Count(T-rchild);n空二叉树的结点个数为空二叉树的结点个数为 0,可直接计算;否则可,可直接计算;否则可分别计算左、右子树的结点个数,再相加得到总分别计算左、右子树的结点个
18、数,再相加得到总结点个数。结点个数。求二叉树高度的递归算法求二叉树高度的递归算法int Height(BiTNode*T)if(T=NULL)return 0;else int m=Height(T-lchild);int n=Height(T-rchild);return(m n)?m+1:n+1;n空树的高度为空树的高度为 0;非空树情形,先计算根结点左;非空树情形,先计算根结点左右子树的高度,取大者再加一得到二叉树高度。右子树的高度,取大者再加一得到二叉树高度。abcecdcc访问访问a进栈进栈c左进左进b访问访问b进栈进栈d左进左进空空退栈退栈d访问访问d左进左进空空退栈退栈c访问访问
19、c左进左进e访问访问e左进左进空空退栈退栈 结束结束利用栈的前序遍历的非递归算法利用栈的前序遍历的非递归算法d dcvoid PreOrder(BinTree T)stack S;InitStack(S);/递归工作栈递归工作栈 BiTNode*p=T;Push(S,NULL);while(p!=NULL)visit(p-data);if(p-rchild!=NULL)Push(S,p-rchild);if(p-lchild!=NULL)p=p-lchild;/进左子树进左子树 else Pop(S,p);/左子树空左子树空,进右子树进右子树 abcdebaadaa左空左空 退栈退栈访问访问左
20、空左空 退栈退栈访问访问退栈退栈访问访问左空左空ec退栈访问退栈访问cc右空右空 退栈访问退栈访问 栈空结束栈空结束利用栈的中序遍历的非递归算法利用栈的中序遍历的非递归算法 void InOrder(BinTree T)stack S;InitStack(S);/递归工作栈递归工作栈 BiTNode*p=T;/初始化初始化 do while(p!=NULL)/子树非空找中序第一个子树非空找中序第一个 Push(S,p);p=p-lchild;/边找边进栈边找边进栈 if(!StackEmpty(S)/栈非空栈非空 Pop(S,p);/子树中序第一个退栈子树中序第一个退栈 visit(p-dat
21、a);/访问之访问之 p=p-rchild;/向右子树走向右子树走 while(p!=NULL|!StackEmpty(S);n后序遍历时使用的栈的结点定义后序遍历时使用的栈的结点定义typedef struct BiTNode*ptr;/结点指针结点指针 enum tag L,R;/该结点退栈标记该结点退栈标记 StackNode;n根结点的根结点的rtag=L,表示从左子树退出,表示从左子树退出,访问右子树。访问右子树。rtag=R,表示表示从右子树退出从右子树退出,访问根。访问根。ptr tagL,R 利用栈的后序遍历的非递归算法利用栈的后序遍历的非递归算法这家医院的每部电梯里都有相应的
22、指示地图,这种服务让病人对医院的满意度大为提高。【本讲小结】14.3 若上述变更不引起卖方履行合同义务的费用或时间的实质性变化时,合同的价格或交货时间将不予调整,但也需卖方书面确认。7、其它资料。收款单位:法正项目管理集团有限公司青海分公司可以和医生正面接触。(2)投标人未按招标文件要求提供资格证明文件或提供的有关资格、资质文件不真实;1.1 为提高企业生产与质量管理水平,确保使用科学、有效的统计方法,及时准确地了解生产与质量情况,为指导生产、改善经营、预测决策提供可靠的依据,特制订本制度;【案例】(二)团组织申报。拟进行换届的团组织,向同级党组织和上级团委提出开展竞争上岗的申请,经同意后按照
23、领导小组办公室的统一安排组织实施。15.1 除了第14条的规定之外,不应对合同条款进行任何变更或修改,除非双方同意并签署书面的合同修改协议,成为本合同不可分割的组成部分,具有与本合同同样的效力。5.1.2 统计方法选定的原则abcdeaLbLaLbRaLdLbRaLdRbRaLbRaLaLaReLcLaReRcLaRcLaRcRaRaR利用栈的后序遍历的非递归算法利用栈的后序遍历的非递归算法void PostOrder(BinTree T)stack S;InitStack(S);StackNode w;BiTNode*p=T;do while(p!=NULL)/向左子树走向左子树走 w.pt
24、r=p;w.tag=L;Push(S,w);p=p-lchild;int succ=1;/继续循环标记继续循环标记 while(succ&!StackEmpty(S)Pop(S,w);p=w.ptr;switch(w.tag)/判断栈顶判断栈顶tag标记标记 case L:w.tag=R;Push(S,w);succ=0;p=p-rchild;break;case R:visit(p-data);while(!StackEmpty(S);二叉树的计数二叉树的计数n由二叉树的前序序列和中序序列可唯一地确定一由二叉树的前序序列和中序序列可唯一地确定一棵二叉树。棵二叉树。n例例,前序序列前序序列 A
25、BHFDECKG 和中序序列和中序序列 HBDFAEKCG,构造构造二叉树过程如下:二叉树过程如下:HBDFEKCGAEKCGABHDFKCGEKCGABHDFEKCGABHFDEABHFDEABHFDCKG612345789612375849n如果前序序列固定不变,给出不同的中序序列,如果前序序列固定不变,给出不同的中序序列,可得到不同的二叉树。可得到不同的二叉树。n问题是:固定前序排列,选择所有可能的中序排问题是:固定前序排列,选择所有可能的中序排列,可以构造出多少种不同的二叉树?列,可以构造出多少种不同的二叉树?123123123123123n例如例如,有有 3 个数据个数据 1,2,3
26、,可得,可得 5 种不同的二种不同的二叉树。它们的前序排列均为叉树。它们的前序排列均为 123,中序序列可能,中序序列可能是是 123,132,213,231,321。n那么,如何推广到一般情形呢?首先,只有一个那么,如何推广到一般情形呢?首先,只有一个结点的不同二叉树只有一个;有结点的不同二叉树只有一个;有 2 个结点的不同个结点的不同二叉树只有二叉树只有 2 种,其他情况呢?种,其他情况呢?b0=1b1=1b2=2b3=5 b4=14有有0个个,1个个,2个个,3个结点的不同二叉树如下个结点的不同二叉树如下n!n!(2n)!1n11n1bCn2nnbibn-i-111n0i1ininbbb
27、计算具有计算具有 n 个结点的不同二叉树的棵数个结点的不同二叉树的棵数nCatalan函数函数n例例512345641131bC363141234567851141bC484线索二叉树线索二叉树(Threaded Binary Tree)n又称为穿线树。又称为穿线树。n通过二叉树遍历,可将二叉树中所有结点的数据通过二叉树遍历,可将二叉树中所有结点的数据排列在一个线性序列中,可以找到某数据在这种排列在一个线性序列中,可以找到某数据在这种排列下它的前驱和后继。排列下它的前驱和后继。n希望不必每次都通过遍历找出这样的线性序列。希望不必每次都通过遍历找出这样的线性序列。只要事先做预处理,将某种遍历顺序
28、下的前驱、只要事先做预处理,将某种遍历顺序下的前驱、后继关系记在树的存储结构中,以后就可以高效后继关系记在树的存储结构中,以后就可以高效地找出某结点的前驱、后继。地找出某结点的前驱、后继。n为此,在二叉树存储结点中增加线索信息。为此,在二叉树存储结点中增加线索信息。线索线索(Thread)n增加前驱增加前驱Pred指针和后继指针和后继Succ指针的二叉树指针的二叉树pred lchild data rchild succabcdepredpredpredsuccsuccsuccDAEBCrootpredpredpredpredsuccsuccsuccsuccn这种设计的缺点是每个结点增加两个指
29、针,当结这种设计的缺点是每个结点增加两个指针,当结点数很大时存储消耗较大。点数很大时存储消耗较大。n改造树结点,将改造树结点,将 pred 指针和指针和 succ 指针压缩到指针压缩到 lchild 和和 rchild 的空闲指针中,并增设两个标志的空闲指针中,并增设两个标志 ltag 和和 rtag,指明指针是指示子女还是前驱后,指明指针是指示子女还是前驱后继。后者称为线索。继。后者称为线索。nltag(或或rtag)=0,表示相应指针指示左子女(或,表示相应指针指示左子女(或右子女结点);右子女结点);nltag(或或rtag)=1,表示相应指针为前驱(或后继)表示相应指针为前驱(或后继)
30、线索。线索。lchild ltag data rtag rchild 中序线索二叉树及其链表表示中序线索二叉树及其链表表示lchild ltag data rtag rchild abcdepredpredpredsuccsuccsuccDAEBCrootpredpredsuccsucc0000111111typedef int TTElemType;typedef struct node int ltag,rtag;struct node*lchild,*rchild;TTElemType data;ThreadNode,*ThreadTree;n注意,线索二叉树在树结点中增加了注意,线索二
31、叉树在树结点中增加了ltag和和rtag,改变了二叉树的结构。改变了二叉树的结构。线索二叉树的结构定义线索二叉树的结构定义通过中序遍历建立中序线索二叉树通过中序遍历建立中序线索二叉树void InThread(ThreadNode*t,ThreadNode*&pre)/预设了一个预设了一个 pre 指针,指示指针,指示 t 的中序前驱,在主的中序前驱,在主/程序中预置为程序中预置为NULL if(t!=NULL)InThread(t-lchild,pre);/递归递归,左子树线索化左子树线索化 if(t-lchild=NULL)t-lchild=pre;t-ltag=1;/建立当前结点建立当前
32、结点 t 的前驱线索的前驱线索 if(pre&pre-rchild=NULL)pre-rchild=t;pre-rtag=1;/建立前驱结点建立前驱结点 pre 的后继线索的后继线索pre=t;/前驱跟上当前指针前驱跟上当前指针InThread(t-rchild,pre);/递归递归,右子树线索化右子树线索化 n使用此函数可以把以使用此函数可以把以 t 为根的子树一次中序线为根的子树一次中序线索化,但中序下最后一个结点的后继线索没有索化,但中序下最后一个结点的后继线索没有加上,指针加上,指针 pre 在退出时正在指示这一结点。在退出时正在指示这一结点。void CreateInThread(T
33、hreadTree T)ThreadNode*pre=NULL;/前驱指针前驱指针 if(T!=NULL)/树非空树非空,线索化线索化 InThread(T,pre);/中序遍历线索二叉树中序遍历线索二叉树 pre-rchild=NULL;pre-rtag=1;/后处理后处理,中序最后一个结点中序最后一个结点 n通过该主程序实现了对二叉树的中序线索化。它通过该主程序实现了对二叉树的中序线索化。它是基于二叉树的中序遍历实现的。是基于二叉树的中序遍历实现的。0 A 0 0 B 0 0 C 0 0 D 0 0 E 0 Tpre=NULLt0 A 0 1 B 0 0 C 0 0 D 0 0 E 0 T
34、pre=NULLt0 A 0 1 B 0 0 C 0 1 D 0 0 E 0 Tpret0 A 0 1 B 0 0 C 0 1 D 1 0 E 0 Tpret0 A 0 1 B 0 0 C 0 1 D 1 1 E 0 Tpret0 A 0 1 B 0 0 C 0 1 D 1 1 E 1 Tpret0 A 0 1 B 0 0 C 1 1 D 1 1 E 1 Tpre后处理寻找结点寻找结点 p 在中序下的后继在中序下的后继if(p-rtag=1)后继为后继为 p-rchildelse /p-rtag!=1 后继为结点后继为结点 p 右子树右子树 q 中的中序下的第一中的中序下的第一 个结点个结点A
35、BDECFHIKGJThreadNode*First(ThreadNode*t)/函数返回以函数返回以*t 为根的线索二叉树中的中序序列下为根的线索二叉树中的中序序列下/的第一个结点的第一个结点 ThreadNode*p=t;while(p-ltag=0)p=p-lchild;return p;/最左下的结点最左下的结点ThreadNode*Next(ThreadNode*p)/函数返回在线索二叉树中结点函数返回在线索二叉树中结点*p 在中序下的后在中序下的后/继结点继结点 if(p-rtag=0)return First(p-rchild);/p-rtag=0,后继为右子树中序第一个结点后继
36、为右子树中序第一个结点 else return p-rchild;/p-rtag=1,直接返回后继线索直接返回后继线索void Inorder(ThreadNode*t)/以以 t 为根的线索二叉树的中序遍历为根的线索二叉树的中序遍历 ThreadNode*p;for(p=First(t);p!=NULL;p=Next(p)cout data ltag=1)前驱为前驱为 p-lchild else/p-leftThread=0 前驱为结点前驱为结点 p 左子树左子树 中序下的最后一个结中序下的最后一个结 点点 ABDECFHIKGJL A B D C Ep-ltag=1?前驱线索前驱线索 =左
37、子女左子女后继为后继为p-lchild=无后继无后继 后继为后继为p-rchildABCED前序线索二叉树前序线索二叉树 p-rtag=1?后继线索后继线索 =右子女右子女后继为后继为 的右子树中的右子树中后序下第一个结点后序下第一个结点 后继为后继为p-rchild=无后继无后继 后继为后继为qABCEDq=p-parentq=NULL?q-rtag=1|q-rchild=p?=后序线索二叉树后序线索二叉树n树的存储表示树的存储表示 双亲表示双亲表示r为了操作实现的方便,有时会规定结点的存放为了操作实现的方便,有时会规定结点的存放顺序。例如,可以按树的前序次序存放树中的顺序。例如,可以按树的
38、前序次序存放树中的各个结点,或按树的层次次序安排所有结点。各个结点,或按树的层次次序安排所有结点。树与树的遍历树与树的遍历ABCDEFGdataparentA B C D E F G-1 0 0 0 1 1 30 1 2 3 4 5 6子女链表表示子女链表表示n无序树情形链表中各结点顺序任意,有序树必须无序树情形链表中各结点顺序任意,有序树必须自左向右链接各个子女结点。自左向右链接各个子女结点。ABCDEFG123456ABCDEFG0123456子女指针表示子女指针表示n一个合理的想法是在结点中存放指向每一个子女一个合理的想法是在结点中存放指向每一个子女结点的指针。但由于各个结点的子女数不同
39、,每结点的指针。但由于各个结点的子女数不同,每个结点设置数目不等的指针,将很难管理。个结点设置数目不等的指针,将很难管理。n为此,设置等长的结点,每个结点包含的指针个为此,设置等长的结点,每个结点包含的指针个数相等,等于树的度(数相等,等于树的度(degree)。)。n这保证结点有足够的指针指向它的所有子女结点。这保证结点有足够的指针指向它的所有子女结点。但可能产生很多空闲指针,造成存储浪费。但可能产生很多空闲指针,造成存储浪费。等数量的链域等数量的链域ABCDEFG data child1child2child3childdABCDEFG 空链域空链域2n+1个子女子女-兄弟表示兄弟表示n也
40、称为树的二叉树表示。结点构造为:也称为树的二叉树表示。结点构造为:nfirstChild 指向该结点的第一个子女结点。无序树指向该结点的第一个子女结点。无序树时,可任意指定一个结点为第一个子女。时,可任意指定一个结点为第一个子女。nnextSibling 指向该结点的下一个兄弟。任一结点指向该结点的下一个兄弟。任一结点在存储时总是有顺序的。在存储时总是有顺序的。n若想找某结点的所有子女,可先找若想找某结点的所有子女,可先找firstChild,再反再反复用复用 nextSibling 沿链扫描。沿链扫描。datafirstChildnextSibling树的左子女树的左子女 -右兄弟表示右兄弟
41、表示 datafchildnsiblingABCDEFGABCDGFE左子女左子女-右兄弟表示的树的结构定义右兄弟表示的树的结构定义typedef int TElemType;typedef struct node TElemType data;struct node*fchild,*nsibling;CSNode,*CSTree;n注意,虽然此定义与二叉树的类似,操作也类似,注意,虽然此定义与二叉树的类似,操作也类似,但语义是不同的。但语义是不同的。n树结构是递归的,可用递归函数实现相应操作。树结构是递归的,可用递归函数实现相应操作。寻找双亲寻找双亲 子女子女 兄弟的操作兄弟的操作TreeN
42、ode*FindParent(CSNode*T,CSNode*p)/在以在以 T 为根的树中找结点为根的树中找结点 p 的双亲的双亲 if(T=NULL|p=NULL)return NULL;CSNode*q=T-fchild,*s;while(q!=NULL&q!=p)/循根的长子的兄弟链循根的长子的兄弟链,递归在子树中搜索递归在子树中搜索 if(s=FindParent(q,p)!=NULL)return s;/在以在以 q 为根的子树找到为根的子树找到 p 的双亲,返回的双亲,返回 q=q-nsibling;if(q!=NULL&q=p)return T;/找到双亲找到双亲 else r
43、eturn NULL;/未找到双亲未找到双亲TreeNode*FindfirstChild(CSNode*p)/在树中找结点在树中找结点 p 的第一个子女的第一个子女 if(p!=NULL)return p-fchild;else return NULL;TreeNode*FindnextSibling(CSNode*p)/在树中找结点在树中找结点 p 的兄弟的兄弟 if(p!=NULL)return p-nsibling;else return NULL;n深度优先遍历深度优先遍历 r先根次序遍历先根次序遍历r后根次序遍历后根次序遍历n广度优先遍历广度优先遍历树的遍历树的遍历ABCDEFGA
44、BCEDGF二叉树表示二叉树表示n当树非空时当树非空时r访问根结点访问根结点r依次先根遍历根的各棵子树依次先根遍历根的各棵子树n树先根遍历树先根遍历 ABEFCDG对应二叉树前序遍历对应二叉树前序遍历 ABEFCDGn树的先根遍历结果与其对应二叉树树的先根遍历结果与其对应二叉树表示的前序遍历结果相同表示的前序遍历结果相同n树的先根遍历可以借助对应二叉树的前序遍历算树的先根遍历可以借助对应二叉树的前序遍历算法实现法实现ABCEDGF树的先根次序遍历树的先根次序遍历ABCDEFG树的先根次序遍历的递归算法树的先根次序遍历的递归算法void PreOrder(CSNode*t)/以指针以指针 t 为
45、根为根,先根次序遍历先根次序遍历 if(t!=NULL)visit(t-data);/访问根结点访问根结点 CSNode*p=t-fchild;/第一棵子树第一棵子树 while(p!=NULL)PreOrder(p);/递归先根遍历子树递归先根遍历子树 p=p-nsibling;n当树非空时当树非空时r依次后根遍历根的各棵子树依次后根遍历根的各棵子树r访问根结点访问根结点n树后根遍历树后根遍历 EFBCGDA对应二叉树中序遍历对应二叉树中序遍历 EFBCGDAn树的后根遍历结果与其对应二叉树树的后根遍历结果与其对应二叉树表示的中序遍历结果相同表示的中序遍历结果相同n树的后根遍历可以借助对应二
46、叉树的中序遍历算树的后根遍历可以借助对应二叉树的中序遍历算法实现法实现ABCEDGF树的后根次序遍历树的后根次序遍历ABCDEFG树的后根次序遍历的递归算法树的后根次序遍历的递归算法void PostOrder(CSNode*t)/以指针以指针 t 为根为根,按后根次序遍历树按后根次序遍历树 if(t!=NULL)CSNode*p=t-fchild;while(p!=NULL)PostOrder(p);p=p-nsibling;visit(t-data);/最后访问根结点最后访问根结点 ABCDEFGvoid LevelOrder(CSTree T)/分层遍历树,算法用到一个队列分层遍历树,算
47、法用到一个队列 Queue Q;InitQueue(Q);CSNode*p;if(T!=NULL)/树不空树不空 EnQueue(Q,T);/根结点进队列根结点进队列广度优先广度优先(层次次序层次次序)遍历遍历ABCEDGFABCDEFG while(!QueueEmpty(Q)DeQueue(Q,p);visit(p-data);/队列中取一个并访问之队列中取一个并访问之 p=p-fchild;/待访问结点的子女结点进队列待访问结点的子女结点进队列 while(p!=NULL)EnQueue(Q,p);p=p-nsibling;森林与二叉树的转换森林与二叉树的转换n将一般树化为二叉树表示就是
48、用树的子女将一般树化为二叉树表示就是用树的子女-兄弟兄弟表示来存储树的结构。表示来存储树的结构。n森林与二叉树表示的转换可以借助树的二叉树表森林与二叉树表示的转换可以借助树的二叉树表示来实现。示来实现。T1 T2 T3AFHT1 T2 T3ABC DGIJEKFBCDEGHIKJABCEDHIKJFG3 棵树的森林棵树的森林各棵树的二叉树表示各棵树的二叉树表示森林的二叉树表示森林的二叉树表示森林转化成二叉树的规则森林转化成二叉树的规则 若若 F 为空,即为空,即 n=0,则对应的二叉树,则对应的二叉树 B 为空为空树。树。若若 F 不空,则不空,则 二叉树二叉树 B 的根是的根是 F 第一棵树
49、第一棵树 T1 的根;的根;其左子树为其左子树为 B(T11,T12,T1m),其中,其中,T11,T12,T1m 是是 T1 的根的子树;的根的子树;其右子树为其右子树为 B(T2,T3,Tn),其中,其中,T2,T3,Tn 是除是除 T1 外其它树构成的森林。外其它树构成的森林。二叉树转换为森林的规则二叉树转换为森林的规则 如果如果 B 为空,则对应的森林为空,则对应的森林 F 也为空。也为空。如果如果 B 非空,则非空,则 F 中第一棵树中第一棵树 T1 的根为的根为 B 的根;的根;T1 的根的子树森林的根的子树森林 T11,T12,T1m 是由是由 B 的根的左子树的根的左子树 LB
50、 转换而来;转换而来;F 中除了中除了 T1 之外其余的树组成的森林之外其余的树组成的森林 T2,T3,Tn 是由是由 B 的根的右子树的根的右子树 RB 转换而转换而成的森林。成的森林。例题例题n设森林中有三棵树,第一、第二和第三棵树中的设森林中有三棵树,第一、第二和第三棵树中的结点个数分别为结点个数分别为 m1,m2 和和 m3。那么在由该森。那么在由该森林转化成的二叉树中根结点的右子树上有(林转化成的二叉树中根结点的右子树上有()个结点。个结点。A.m1+m2 B.m2+m3 C.m3+m1 D.m1+m2+m3【解答解答】在由森林转化成的二叉树中根结点的右子在由森林转化成的二叉树中根结