1、第六章第六章树和二叉树树和二叉树教学目的和要求教学目的和要求 1、熟练掌握二叉树的结构特点,了解相应的证明。、熟练掌握二叉树的结构特点,了解相应的证明。2、熟悉二叉树的各种存储结构的特点及适用范围。、熟悉二叉树的各种存储结构的特点及适用范围。3、掌握二叉树遍历的递归与非递归算法。、掌握二叉树遍历的递归与非递归算法。4、掌握二叉线索树的相关算法。、掌握二叉线索树的相关算法。5、熟悉树的各种存储结构及特点,掌握树和森林、熟悉树的各种存储结构及特点,掌握树和森林与二叉树的方法。与二叉树的方法。6、了解最优树的特性,掌握最优树和哈夫曼编码、了解最优树的特性,掌握最优树和哈夫曼编码的方法。的方法。1数据
2、的逻辑结构 2、数据的存储结构 3、数据的运算:检索、排序、插入、删除、修改等。A线性结构 B非线性结构A 顺序存储 B 链式存储 线性表栈队树形结构图形结构数据结构的三个主要问题 树形结构树形结构全校学生档案管理的组织方式全校学生档案管理的组织方式ABCDEFGH树形结构树形结构 结点间具有分层次的连接关系结点间具有分层次的连接关系HBCDEFGA6.1 树的类型定义树的类型定义6.2 6.2 二叉树的类型定义二叉树的类型定义6.3 二叉树的存储结构二叉树的存储结构6.4 二叉树的遍历二叉树的遍历6.5 线索二叉树线索二叉树6.6 树和森林的表示方法树和森林的表示方法6.7 树和森林的遍历树
3、和森林的遍历6.8 哈夫曼树与哈夫曼编码哈夫曼树与哈夫曼编码6.1 树的类型定义树的类型定义数据对象数据对象 D:D是具有相同特性的数据元素的集合。是具有相同特性的数据元素的集合。若若D为空集,则称为空树为空集,则称为空树。否则否则:(1)在在D中存在唯一的称为根的数据元素中存在唯一的称为根的数据元素root;(2)当当n1时,其余结点可分为时,其余结点可分为m(m0)个互个互 不相交的有限集不相交的有限集T1,T2,Tm,其中每一,其中每一 棵子集本身又是一棵符合本定义的树,棵子集本身又是一棵符合本定义的树,称为根称为根root的子树。的子树。数据关系数据关系 R:ABCDEFGHIJMKL
4、A(B(E,F(K,L),C(G),D(H,I,J(M)T1T3T2树根例如例如:基本操作:基本操作:查查 找找 类类 插插 入入 类类删删 除除 类类 Root(T)/求树的根结点求树的根结点 查找类:查找类:Value(T,cur_e)/求当前结点的元素值求当前结点的元素值 Parent(T,cur_e)/求当前结点的双亲结点求当前结点的双亲结点LeftChild(T,cur_e)/求当前结点的最左孩子求当前结点的最左孩子 RightSibling(T,cur_e)/求当前结点的右兄弟求当前结点的右兄弟TreeEmpty(T)/判定树是否为空树判定树是否为空树 TreeDepth(T)/求
5、树的深度求树的深度TraverseTree(T,Visit()/遍历遍历InitTree(&T)/初始化置空树初始化置空树 插入类:插入类:CreateTree(&T,definition)/按定义构造树按定义构造树Assign(T,cur_e,value)/给当前结点赋值给当前结点赋值InsertChild(&T,&p,i,c)/将以将以c为根的树插入为结点为根的树插入为结点p的第的第i棵子树棵子树 ClearTree(&T)/将树清空将树清空 删除类:删除类:DestroyTree(&T)/销毁树的结构销毁树的结构DeleteChild(&T,&p,i)/删除结点删除结点p的第的第i棵子树
6、棵子树对比对比树型结构树型结构和和线性结构线性结构的结构特点的结构特点线性结构线性结构树型结构树型结构第一个数据元素第一个数据元素 (无前驱无前驱)根结点根结点 (无前驱无前驱)最后一个数据元素最后一个数据元素 (无后继无后继)多个叶子结点多个叶子结点 (无后继无后继)其它数据元素其它数据元素(一个前驱、一个前驱、一个后继一个后继)其它数据元素其它数据元素(一个前驱、一个前驱、多个后继多个后继)基基 本本 术术 语语结点结点:结点的度结点的度:树的度树的度:叶子结点叶子结点:分支结点分支结点:数据元素+若干指向子树的分支分支的个数树中所有结点的度的最大值度为零的结点度大于零的结点DHIJM(从
7、根到结点的)路径路径:孩子孩子结点、双亲双亲结点兄弟兄弟结点、堂兄弟祖先祖先结点、子孙子孙结点结点的层次结点的层次:树的深度:树的深度:由从根根到该结点所经分支和结点构成ABCDEFGHIJMKL假设根结点的层次为1,第l 层的结点的子树根结点的层次为l+1树中叶子结点所在的最大层次任何一棵非空树是一个二元组 Tree=(root,F)其中:root 被称为根结点 F 被称为子树森林森林:森林:是m(m0)棵互不相交的树的集合ArootBCDEFGHIJMKLF6.2 二叉树的类型定义二叉树的类型定义 二叉树或为空树空树,或是由一个根结根结点点加上两棵两棵分别称为左子树左子树和右子树的、互不交
8、的互不交的二叉树二叉树组成。ABCDEFGHK根结点左子树右子树二叉树的五种基本形态:二叉树的五种基本形态:N空树空树只含根结点只含根结点NNNLRR右子树为空树右子树为空树L左子树为空树左子树为空树左右子左右子树均不树均不为空树为空树 二叉树的主要基本操作二叉树的主要基本操作:查查 找找 类类插插 入入 类类删删 除除 类类 Root(T);Value(T,e);Parent(T,e);LeftChild(T,e);RightChild(T,e);LeftSibling(T,e);RightSibling(T,e);BiTreeEmpty(T);BiTreeDepth(T);PreOrder
9、Traverse(T,Visit();InOrderTraverse(T,Visit();PostOrderTraverse(T,Visit();LevelOrderTraverse(T,Visit();InitBiTree(&T);Assign(T,&e,value);CreateBiTree(&T,definition);InsertChild(T,p,LR,c);ClearBiTree(&T);DestroyBiTree(&T);DeleteChild(T,p,LR);二叉树二叉树的重要特性的重要特性 性质性质 1:在二叉树的第 i 层上至多有2i-1 个结点。(i1)用归纳法证明用归纳
10、法证明:归纳基归纳基:归纳假设:归纳假设:归纳证明:归纳证明:i=1 层时,只有一个根结点:2i-1=20=1;假设对所有的 j,1 j i,命题成立;二叉树上每个结点至多有两棵子树,则第 i 层的结点数=2i-2 2=2i-1。性质性质 2:深度为 k 的二叉树上至多含 2k-1 个结点(k1)。证明:证明:基于上一条性质,深度为 k 的二叉树上的结点数至多为 20+21+2k-1=2k-1。性质性质 3:对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0=n2+1。证明:证明:设设 二叉树上结点总数 n=n0+n1+n2又又 二叉树上分支总数 b=n
11、1+2n2 而 b=n-1=n0+n1+n2-1由此,由此,n0=n2+1。两类两类特殊特殊的二叉树:的二叉树:满二叉树满二叉树:指的是深度为k且含有2k-1个结点的二叉树。完全二叉树完全二叉树:树中所含的 n 个结点和满二叉树中编号编号为为 1 至至 n 的结点的结点一一对应。123456789 10 11 12 13 14 15abcdefghij性质性质 4:具有 n 个结点的完全二叉树的深度深度为 log2n +1。证明:证明:设设完全二叉树的深度为 k 则根据第二条性质得 2k-1 n 2k 即 k-1 log2 n n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子左孩子结
12、点;(3)若 2i+1n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子右孩子结点。()例题例题1:若一树二叉共有若一树二叉共有1001个结点,且无度为个结点,且无度为1的结的结点,则叶子结点的个数为(点,则叶子结点的个数为()。)。例题例题2:将一棵有将一棵有100个结点的完全二叉树从上到下,个结点的完全二叉树从上到下,从左到右依次对结点进行编号,根结点的编从左到右依次对结点进行编号,根结点的编号为号为1,则编号为,则编号为49的结点的孩子编号为(的结点的孩子编号为()。6.3 二叉树的存储结构二叉树的存储结构二、二叉树的链式二、二叉树的链式 存储表示存储表示一、一、二叉树的顺
13、序二叉树的顺序 存储表示存储表示#define MAX_TREE_SIZE 100 /二叉树的最大结点数typedef TElemType SqBiTreeMAX_ TREE_SIZE;/0号单元存储根结点SqBiTree bt;一、一、二叉树的顺序存储表示二叉树的顺序存储表示例如例如:ABCDEF A B D C E F 0 1 2 3 4 5 6 7 8 9 10 11 12 131401326顺序存储结构仅适用于完全二叉树!顺序存储结构仅适用于完全二叉树!二、二叉树的链式存储表示二、二叉树的链式存储表示1.1.二叉链表二叉链表2三叉链表三叉链表3 3双亲链表双亲链表4线索链表线索链表AD
14、EBCF rootlchild data rchild结点结构结点结构:1.1.二叉链表二叉链表typedef struct BiTNode /结点结构结点结构 TElemType data;struct BiTNode *lchild,*rchild;/左右孩子指针 BiTNode,*BiTree;lchild data rchild结点结构结点结构:C 语言的类型描述如下语言的类型描述如下:ADEBCF root 2三叉链表三叉链表parent lchild data rchild结点结构结点结构:typedef struct TriTNode /结点结构结点结构 TElemType da
15、ta;struct TriTNode *lchild,*rchild;/左右孩子指针 struct TriTNode *parent;/双亲指针 TriTNode,*TriTree;parent lchild data rchild结点结构结点结构:C 语言的类型描述如下语言的类型描述如下:0123456B2C0A-1D2E3F4 data parent结点结构结点结构:3 3双亲链表双亲链表LRTagLRRRL typedef struct BPTNode /结点结构结点结构 TElemType data;int *parent;/指向双亲的指针 char LRTag;/左、右孩子标志域 B
16、PTNode typedef struct BPTree/树结构树结构 BPTNode nodesMAX_TREE_SIZE;int num_node;/结点数目 int root;/根结点的位置 BPTree6.4二叉树的遍历二叉树的遍历一、问题的提出一、问题的提出二、先左后右的遍历算法二、先左后右的遍历算法三、算法的递归描述三、算法的递归描述四、中序遍历算法的非递归描述四、中序遍历算法的非递归描述五五、遍历算法的应用举例遍历算法的应用举例 如何顺着某一条搜索路径巡访巡访二叉树中的结点,使得每个结点均被访均被访问一次问一次,而且仅被访问一次仅被访问一次。一、问题的提出一、问题的提出 “访问访
17、问”的含义可以很广,如:输出结点的信息等。“遍历遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。而二叉树是非线性结构,每个结点有两个后继每个结点有两个后继,则存在如何遍历存在如何遍历即按什么样的搜索搜索路径路径遍历的问题。对对“二叉树二叉树”而言,可以有而言,可以有三条搜索路径:三条搜索路径:1先上后下先上后下的按层次遍历;2先左先左(子树)后右后右(子树)的遍历;3先右先右(子树)后左后左(子树)的遍历。二、先左后右的遍历算法二、先左后右的遍历算法先先序(根)的遍历算法中中序(根)的遍历算法后后序(根)的遍历算法 若二叉树为空树,
18、则空操作;否则,(1)访问根结点;(2)先序遍历左子树;(3)先序遍历右子树。先序(根)的遍历算法:先序(根)的遍历算法:若二叉树为空树,则空操作;否则,(1)中序遍历左子树;(2)访问根结点;(3)中序遍历右子树。中序(根)的遍历算法:中序(根)的遍历算法:若二叉树为空树,则空操作;否则,(1)后序遍历左子树;(2)后序遍历右子树;(3)访问根结点。后序(根)的遍历算法:后序(根)的遍历算法:课堂提问:有以下结构的二叉树有以下结构的二叉树 写出其先序、中序和后序遍历的序列写出其先序、中序和后序遍历的序列ABCDE三、算法的递归描述三、算法的递归描述void Preorder(BiTree T
19、,void(*visit)(TElemType&e)/先序遍历二叉树 if(T)visit(T-data);/访问结点 Preorder(T-lchild,visit);/遍历左子树 Preorder(T-rchild,visit);/遍历右子树 四、中序遍历算法的非递归描述四、中序遍历算法的非递归描述BiTNode*GoFarLeft(BiTree T,Stack*S)if(!T)return NULL;while(T-lchild)Push(S,T);T=T-lchild;return T;void Inorder_I(BiTree T,void(*visit)(TelemType&e)S
20、tack*S;t=GoFarLeft(T,S);/找到最左下的结点 while(t)visit(t-data);if(t-rchild)t=GoFarLeft(t-rchild,S);else if(!StackEmpty(S)/栈不空时退栈 t=Pop(S);else t=NULL;/栈空表明遍历结束 /while/Inorder_I 五五、遍历算法的应用举例遍历算法的应用举例1、统计二叉树中叶子结点的个数、统计二叉树中叶子结点的个数 (先序遍历先序遍历)2、求二叉树的深度、求二叉树的深度(后序遍历后序遍历)3、复制二叉树、复制二叉树(后序遍历后序遍历)4 4、建立二叉树的存储结构、建立二叉
21、树的存储结构1、统计二叉树中叶子结点的个数、统计二叉树中叶子结点的个数算法基本思想算法基本思想:先序(或中序或后序)遍历二叉树,在遍历过程中查找叶子结点,并计数。由此,需在遍历算法中增添一个需在遍历算法中增添一个“计数计数”的参数,的参数,并将算法中“访问结点”的操作改为:若是叶子,则计数器增若是叶子,则计数器增1 1。void CountLeaf(BiTree T,int&count)if(T)if(!T-lchild)&(!T-rchild)count+;/对叶子结点计数 CountLeaf(T-lchild,count);CountLeaf(T-rchild,count);/if/Cou
22、ntLeaf2、求二叉树的深度、求二叉树的深度(后序遍历后序遍历)算法基本思想算法基本思想:从二叉树深度的定义可知,二叉树的二叉树的深度应为其左、右子树深度的最大值加深度应为其左、右子树深度的最大值加1 1。由此,需先分别求得左、右子树的深度,需先分别求得左、右子树的深度,算法中“访问结点”的操作为:求得左、求得左、右子树深度的最大值,然后加右子树深度的最大值,然后加 1 1。首先分析二叉树的深度二叉树的深度和它的左左、右子右子树深度树深度之间的关系。int Depth(BiTree T)/返回二叉树的深度 if(!T)depthval=0;else depthLeft=Depth(T-lch
23、ild);depthRight=Depth(T-rchild);depthval=1+(depthLeft depthRight?depthLeft:depthRight);return depthval;3、复制二叉树、复制二叉树其基本操作为:生成一个结点。其基本操作为:生成一个结点。根元素根元素T左子树左子树右子树右子树根元素根元素NEWT左子树左子树右子树右子树左子树左子树右子树右子树(后序遍历后序遍历)BiTNode*GetTreeNode(TElemType item,BiTNode*lptr,BiTNode*rptr)if(!(T=(BiTNode*)malloc(sizeof(B
24、iTNode)exit(OVERFLOW);T-data=item;T-lchild=lptr;T-rchild=rptr;return T;生成一个二叉树的结点生成一个二叉树的结点(其数据域为其数据域为item,左指针域为左指针域为lptr,右指针域为右指针域为rptr)BiTNode*CopyTree(BiTNode*T)if(!T)return NULL;if(T-lchild)newlptr=CopyTree(T-lchild);/复制左子树 else newlptr=NULL;if(T-rchild)newrptr=CopyTree(T-rchild);/复制右子树 else new
25、rptr=NULL;newT=GetTreeNode(T-data,newlptr,newrptr);return newT;/CopyTreeABCDEFGHK D C B H K G F E A例如:下列二叉树例如:下列二叉树的复制过程如下:的复制过程如下:newT4 4、建立二叉树的存储、建立二叉树的存储结构结构不同的定义方法相应有不同的不同的定义方法相应有不同的存储结构的建立算法存储结构的建立算法以字符串“A!”表示 以字符串的形式以字符串的形式 根根 左子树左子树 右子树右子树定义一棵二叉树定义一棵二叉树例如:ABCD以字符“!”表示A(B(!,C(!,!),D(!,!)空树空树只含
26、一个根结点只含一个根结点的二叉树的二叉树A以下列字符串表示Status CreateBiTree(BiTree&T)scanf(&ch);if(ch=!)T=NULL;else if(!(T=(BiTNode*)malloc(sizeof(BiTNode)exit(OVERFLOW);T-data=ch;/生成根结点 CreateBiTree(T-lchild);/构造左子树 CreateBiTree(T-rchild);/构造右子树 return OK;/CreateBiTree 仅知二叉树的先序序列“abcdefg”不能唯一确定一棵二叉树,由二叉树的先序和中序序列建树由二叉树的先序和中序序
27、列建树 如果同时已知二叉树的中序序列“cbdaegf”,则会如何?二叉树的先序序列二叉树的中序序列左子树左子树左子树左子树 右子树右子树右子树右子树根根根根a b c d e f gc b d a e g f例如例如:aab bccddeeffggabcdefg先序序列中序序列void CrtBT(BiTree&T,char pre,char ino,int ps,int is,int n)/已知preps.ps+n-1为二叉树的先序序列,/inois.is+n-1为二叉树的中序序列,本算 /法由此两个序列构造二叉链表 if(n=0)T=NULL;else k=Search(ino,preps
28、);/在中序序列中查询 if(k=-1)T=NULL;else /CrtBT T=(BiTNode*)malloc(sizeof(BiTNode);T-data=preps;if(k=is)T-Lchild=NULL;else CrtBT(T-Lchild,pre,ino,ps+1,is,k-is);if(k=is+n-1)T-Rchild=NULL;else CrtBT(T-Rchild,pre,ino,ps+(k-is)+1,k+1,n-(k-is)-1);例题:例题:1、编写递归算法:对于二叉树中每一个、编写递归算法:对于二叉树中每一个元素值为元素值为x的结点,删除以它为根的子树,的结点
29、,删除以它为根的子树,并释放相应的空间。并释放相应的空间。6.5线索二叉树线索二叉树 何谓线索二叉树?何谓线索二叉树?线索链表的遍历算法线索链表的遍历算法 如何建立线索链表?如何建立线索链表?一、一、何谓线索二叉树?何谓线索二叉树?遍历二叉树的结果是,求得结点的一个线性序列。ABCDEFGHK例如:先序先序序列:A B C D E F G H K中序中序序列:B D C A H G K F E后序后序序列:D C B H K G F E A指向该线性序列中的“前驱”和“后继”的指针指针,称作“线索线索”与其相应的二叉树,称作“线索二叉树线索二叉树”包含“线索”的存储结构,称作“线索链线索链表表
30、”A B C D E F G H K D C B E 对对线索链表线索链表中结点的约定:中结点的约定:在二叉链表的结点中增加两个标志域增加两个标志域,并作如下规定:若该结点的左子树不空,若该结点的左子树不空,则Lchild域的指针指向其左子树,且左标志域的值为“指针 Link”;否则,Lchild域的指针指向其“前驱”,且左标志的值为“线索 Thread”。若该结点的右子树不空,若该结点的右子树不空,则rchild域的指针指向其右子树,且右标志域的值为“指针 Link”;否则,rchild域的指针指向其“后继”,且右标志的值为“线索 Thread”。如此定义的二叉树的存储结构称作如此定义的二叉
31、树的存储结构称作“线索链表线索链表”。typedef struct BiThrNod TElemType data;struct BiThrNode *lchild,*rchild;/左右指针 PointerThr LTag,RTag;/左右标志 BiThrNode,*BiThrTree;线索链表的类型描述:typedef enum Link,Thread PointerThr;/Link=0:指针,Thread=1:线索线索二叉树的头结点线索二叉树的头结点 添加一个头结点,并令其lchild域的指针指向二叉树的根结点,其rchild域的指针指向中序遍历时访问的最后一个结点;反之,令二叉树中序
32、序列中的第一个结点的lchild域指针和最后一个结点rchild域 的指针均指向头结点。这好比为二叉树建立了一个双向线索链表。(参考教材P133)二、线索链表的遍历算法二、线索链表的遍历算法:for(p=firstNode(T);p;p=Succ(p)Visit(p);由于在线索链表中添加了遍历中得到的“前驱”和“后继”的信息,从而简化了遍历的算法。例如例如:对中序线索化链表的遍历算法对中序线索化链表的遍历算法 中序遍历的第一个结点中序遍历的第一个结点?在中序线索化链表中结点的后继在中序线索化链表中结点的后继?左子树上处于“最左下最左下”(没有左子树)的结点。若若无右子树,则为则为后继线索后继
33、线索所指结点;否则为否则为对其右子树右子树进行中序遍历遍历时访问的第一个结点。第一个结点。void InOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType e)p=T-lchild;/p指向根结点 while(p!=T)/空树或遍历结束时,p=T while(p-LTag=Link)p=p-lchild;/第一个结点 if(!Visit(p-data)return ERROR;while(p-RTag=Thread&p-rchild!=T)p=p-rchild;Visit(p-data);/访问后继结点 p=p-rchild;/p进至其右子
34、树根 /InOrderTraverse_Thr 在中序遍历过程中修改结点的在中序遍历过程中修改结点的左、右指针域,以保存当前访问结左、右指针域,以保存当前访问结点的点的“前驱前驱”和和“后继后继”信息。遍历过信息。遍历过程中,附设指针程中,附设指针pre,并始终保持指并始终保持指针针pre指向当前访问的、指针指向当前访问的、指针p所指所指结点的前驱。结点的前驱。三、如何建立线索链表?三、如何建立线索链表?void InThreading(BiThrTree p)if(p)/对以p为根的非空二叉树进行线索化 InThreading(p-lchild);/左子树线索化 if(!p-lchild)/
35、建前驱线索 p-LTag=Thread;p-lchild=pre;if(!pre-rchild)/建后继线索 pre-RTag=Thread;pre-rchild=p;pre=p;/保持 pre 指向 p 的前驱 InThreading(p-rchild);/右子树线索化 /if/InThreadingStatus InOrderThreading(BiThrTree&Thrt,BiThrTree T)/构建中序线索链表 if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode)exit(OVERFLOW);Thrt-LTag=Link;Thrt-RTag=Th
36、read;Thrt-rchild=Thrt;/添加头结点 return OK;/InOrderThreading if(!T)Thrt-lchild=Thrt;else Thrt-lchild=T;pre=Thrt;InThreading(T);pre-rchild=Thrt;/处理最后一个结点 pre-RTag=Thread;Thrt-rchild=pre;课堂练习 习题集:P436.56 6.6 树和森林树和森林 的表示方法的表示方法6.6.1 树的三种存储结构树的三种存储结构一、一、双亲表示法双亲表示法二、二、孩子链表表示法孩子链表表示法三、三、树的二叉链表树的二叉链表(孩子孩子-兄弟)
37、兄弟)存储表示法存储表示法ABCDEFG0 A -11 B 02 C 03 D 04 E 2 5 F 26 G 5r=0n=6data parent一、双亲表示法一、双亲表示法:typedef struct PTNode Elem data;int parent;/双亲位置域 PTNode;data parent#define MAX_TREE_SIZE 100结点结构结点结构:C语言的类型描述语言的类型描述:typedef struct PTNode nodes MAX_TREE_SIZE;int r,n;/根结点的位置和结点个数 PTree;树结构树结构:ABCDEFG0 A -11 B
38、02 C 03 D 04 E 25 F 26 G 4r=0n=6 data firstchild 1 2 34 56二、孩子链表表示法二、孩子链表表示法:typedef struct CTNode int child;struct CTNode*next;*ChildPtr;孩子结点结构孩子结点结构:child nextC语言的类型描述语言的类型描述:typedef struct Elem data;ChildPtr firstchild;/孩子链的头指针 CTBox;双亲结点结构双亲结点结构 data firstchildtypedef struct CTBox nodesMAX_TREE_
39、SIZE;int n,r;/结点数和根结点的位置 CTree;树结构树结构:ABCDEFG AB C E D F Groot AB C E D F G 三、树的二叉链表三、树的二叉链表(孩子孩子-兄弟)存储表示法兄弟)存储表示法typedef struct CSNode Elem data;struct CSNode *firstchild,*nextsibling;CSNode,*CSTree;C语言的类型描述语言的类型描述:结点结构结点结构:firstchild data nextsibling 6.6.2 森林和二叉树的转换森林和二叉树的转换设设森林森林 F=(T1,T2,Tn);T1=
40、(root,t11,t12,t1m);二叉树二叉树 B=(LBT,Node(root),RBT);由于二叉树可以用二叉链表表示,为了使一般树由于二叉树可以用二叉链表表示,为了使一般树也能用二叉链表表示,必须找出树与二叉树之间的关也能用二叉链表表示,必须找出树与二叉树之间的关系。系。这样,给定一棵树,可以找到唯一的一棵二叉树这样,给定一棵树,可以找到唯一的一棵二叉树与之对应。与之对应。方法:方法:对每个孩子进行从左到右的排序;对每个孩子进行从左到右的排序;在兄弟之间加一条连线;在兄弟之间加一条连线;对每个结点,除了左孩子外,去除其与其余孩对每个结点,除了左孩子外,去除其与其余孩子之间的联系;子之
41、间的联系;以根结点为轴心,将整个树顺时针转以根结点为轴心,将整个树顺时针转45度。度。1、将树转换成二叉树、将树转换成二叉树的转换规则为:I A B C DE F G H(b)A B CD E G H FI(a)树转换为二叉树树转换为二叉树ABEFCDGHI(d)ABCDEFGHI(c)2、由森林转换成二叉树、由森林转换成二叉树的转换规则为:若 F=,则 B=;否则,由 ROOT(T1)对应得到 Node(root);由(t11,t12,t1m)对应得到 LBT;由(T2,T3,Tn)对应得到 RBT。森林转换为二叉树森林转换为二叉树ADCBEFHIGJEFADCBHIGJADCBEFHIGJ
42、ADCBEFHIGJ3、由二叉树转换为森林、由二叉树转换为森林的转换规则为:若 B=,则 F=;否则,由 Node(root)对应得到 ROOT(T1);由LBT 对应得到(t11,t12,,t1m);由RBT 对应得到(T2,T3,Tn)。将二叉树转换成树或森林的方法如下:将二叉树转换成树或森林的方法如下:1.若某结点是其若某结点是其双亲双亲的的左孩子左孩子,则把该结点的右孩子、则把该结点的右孩子、右孩子的右孩子右孩子的右孩子都与该结点的都与该结点的双亲双亲结点用线连起结点用线连起来来;2.删除原二叉树中所有的双亲结点与右孩子结点的连删除原二叉树中所有的双亲结点与右孩子结点的连线线.3.整理
43、步骤整理步骤1、2所得到的树或森林所得到的树或森林,使结构层次分明使结构层次分明.将二叉树转换为树或森林将二叉树转换为树或森林的另一种描述:ABEFCDGHI(a)ABEFCDGHI(b)将二叉树转换为树或森林将二叉树转换为树或森林ABEFCDGHI(c)若某结点是其若某结点是其双亲双亲的的左孩子左孩子,则把该则把该结点的右孩子、右结点的右孩子、右孩子的右孩子孩子的右孩子都都与该结点的与该结点的双亲双亲结结点用线连起来点用线连起来;删除原二叉树中所删除原二叉树中所有的双亲结点与右有的双亲结点与右孩子结点的连线孩子结点的连线.由此,树的各种操作均可对应二叉树的操作来完成。应当注意的是,应当注意的
44、是,和树对应的二叉树,其左、右子树的概念已改变为:左是孩子,右是兄弟。左是孩子,右是兄弟。6.7树和森林的遍历树和森林的遍历一、树的遍历一、树的遍历二、森林的遍历二、森林的遍历三、树的遍历的应用三、树的遍历的应用树的遍历可有三条搜索路径树的遍历可有三条搜索路径:按层次遍历按层次遍历:先根先根(次序次序)遍历遍历:后根后根(次序次序)遍历遍历:若树不空,则先访问根结点,然后若树不空,则先访问根结点,然后依次先根遍历各棵子树。依次先根遍历各棵子树。若树不空,则先依次后根遍历各棵若树不空,则先依次后根遍历各棵子树,然后访问根结点。子树,然后访问根结点。若树不空,则自上而下自左至右若树不空,则自上而下
45、自左至右访问树中每个结点。访问树中每个结点。A B C DE F G H I J K 先根遍历时顶点先根遍历时顶点的访问次序:的访问次序:A B E F C D G H I J K 后根遍历时顶点后根遍历时顶点的访问次序:的访问次序:E F B C I J K H G D A 层次遍历时顶点层次遍历时顶点的访问次序:的访问次序:A B C D E F G H I J K B C DE F G H I J K1森林中第一棵树的根结点;2森林中第一棵树的子树森林;3森林中其它树构成的森林。森林由三部分构成:若森林不空,则访问访问森林中第一棵树的根结点;先序遍历先序遍历森林中第一棵树的子树森林;先序
46、遍历先序遍历森林中(除第一棵树之外)其 余树构成的森林。1.先序遍历先序遍历森林的遍历森林的遍历即:依次从左至右依次从左至右对森林中的每一棵树树进行先根遍历先根遍历。中序遍历中序遍历 若森林不空,则中序遍历中序遍历森林中第一棵树的子树森林;访问访问森林中第一棵树的根结点;中序遍历中序遍历森林中(除第一棵树之外)其 余树构成的森林。即:依次从左至右依次从左至右对森林中的每一棵树树进行后根遍历后根遍历。树的遍历和二叉树遍历树的遍历和二叉树遍历的对应关系的对应关系?先根遍历先根遍历后根遍历后根遍历树树二叉树二叉树森林森林先序遍历先序遍历先序遍历先序遍历中序遍历中序遍历后序遍历后序遍历设树的存储结构为
47、孩子兄弟链表设树的存储结构为孩子兄弟链表typedef struct CSNode Elem data;struct CSNode*firstchild,*nextsibling;CSNode,*CSTree;一、求树的深度一、求树的深度二、输出树中所有从根到叶子的路径二、输出树中所有从根到叶子的路径三、建树的存储结构三、建树的存储结构int TreeDepth(CSTree T)if(!T)return 0;else h1=TreeDepth(T-firstchild);h2=TreeDepth(T-nextsibling);/TreeDepthreturn(max(h1+1,h2);一、求
48、树的深度的算法:一、求树的深度的算法:二、二、输出树中所有从根到叶子的路径的算法输出树中所有从根到叶子的路径的算法:A B C DE F G H I J K例如:对左图所示的树,其输出结果应为:A B EA B FA CA D G H IA D G H JA D G H Kvoid AllPath(Bitree T,Stack&S)if(T)Push(S,T-data);if(!T-Lchild&!T-Rchild)PrintStack(S);else AllPath(T-Lchild,S);AllPath(T-Rchild,S);Pop(S);/if(T)/AllPath/输出二叉树上从根到
49、所有叶子结点的路径输出二叉树上从根到所有叶子结点的路径void OutPath(Bitree T,Stack&S)while(!T)Push(S,T-data);if(!T-firstchild)Printstack(s);else OutPath(T-firstchild,s);Pop(S);T=T-nextsibling;/while/OutPath/输出森林中所有从根到叶的路径三、建树的存储结构的算法三、建树的存储结构的算法:和二叉树类似,不同的定义相应有不同的算法。假设以二元组(F,C)的形式自上而自上而下下、自左而右自左而右依次输入树的各边,建立树的孩子孩子-兄弟链表兄弟链表。ABC
50、DEFG例如例如:对下列所示树的输入序列应为:(#,A)(A,B)(A,C)(A,D)(C,E)(C,F)(E,G)ABCD(#,A)(A,B)(A,C)(A,D)(C,E)可见,算法中需要一个队列队列保存已建好的结点的指针。指针。void CreatTree(CSTree&T)T=NULL;for(scanf(&fa,&ch);ch!=;scanf(&fa,&ch);)p=GetTreeNode(ch);/创建结点EnQueue(Q,p);/指针入队列if(fa=)T=p;/所建为根结点 else /非根结点的情况 /for/CreateTree GetHead(Q,s);/取队列头元素(指