《数据结构第六章树和二叉树.ppt》由会员分享,可在线阅读,更多相关《数据结构第六章树和二叉树.ppt(123页珍藏版)》请在三一办公上搜索。
1、第六章 树和二叉树,6.1 树的类型定义,6.2 二叉树的类型定义,6.3 二叉树的存储结构,6.4 二叉树的遍历,6.5 线索二叉树,6.6 树和森林的表示方法,6.7 树和森林的遍历,6.8 哈夫曼树与哈夫曼编码,6.1 树的类型定义,数据对象 D:,D是具有相同特性的数据元素的集合。,若D为空集,则称为空树;否则:(1)在D中存在唯一的称为根的数据元素root,(2)当n1时,其余结点可分为m(m0)个互 不相交的有限集T1,T2,Tm,其中每一 棵子集本身又是一棵符合本定义的树,称为根root的子树。,数据关系 R:,A,B,C,D,E,F,G,H,I,J,M,K,L,例如:,基 本
2、术 语,结点:,结点的度:,树的度:,叶子结点:,分支结点:,数据元素+若干指向子树的分支,分支的个数,树中所有结点的度的最大值,度为零的结点,度大于零的结点,D,H,I,J,M,(从根到结点的)路径:,孩子结点、双亲结点、兄弟结点、堂兄弟祖先结点、子孙结点,结点的层次:,树的深度:,由从根到该结点所经分支和结点构成,假设根结点的层次为1,第l 层的结点的子树根结点的层次为l+1,树中叶子结点所在的最大层次,任何一棵非空树是一个二元组 Tree=(root,F)其中:root 被称为根结点,F 被称为子树森林,森林:,是 m(m0)棵互不相交的树的集合,A,root,F,()有确定的根;()树
3、根和子树根之间为有向关系。,有向树:,有序树:,子树之间存在确定的次序关系。,无序树:,子树之间不存在确定的次序关系。,对比树型结构和线性结构的结构特点,线性结构,树型结构,第一个数据元素(无前驱),根结点(无前驱),最后一个数据元素(无后继),多个叶子结点(无后继),其它数据元素(一个前驱、一个后继),其它数据元素(一个前驱、多个后继),6.2 二叉树的类型定义,二叉树或为空树;或是由一个根结点加上两棵分别称为左子树和右子树的、互不交的二叉树组成。,根结点,左子树,右子树,E,F,二叉树的五种基本形态:,N,空树,只含根结点,N,N,N,L,R,R,右子树为空树,L,左子树为空树,左右子树均
4、不为空树,二叉树的重要特性,性质 1:在二叉树的第 i 层上至多有2i-1 个结点。(i1),用归纳法证明:归纳基:归纳假设:归纳证明:,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,证明:,设 二叉
5、树上结点总数 n=n0+n1+n2,又 二叉树上分支总数 b=n1+2n2,而 b=n-1=n0+n1+n2-1,由此,n0=n2+1,两类特殊的二叉树:,满二叉树:指的是深度为k且含有2k-1个结点的二叉树。,完全二叉树:树中所含的 n 个结点和满二叉树中编号为 1 至 n 的结点一一对应。,性质 4:具有 n 个结点的完全二叉树的深度为 log2n+1,证明:,设 完全二叉树的深度为 k,则根据第二条性质得 2k-1 n 2k,即 k-1 log2 n k,因为 k 只能是整数,因此,k=log2n+1,性质 5:,若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,
6、则对完全二叉树中任意一个编号为 i 的结点:(1)若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 i/2 的结点为其双亲结点;(2)若 2in,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;(3)若 2i+1n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。,6.3 二叉树的存储结构,二、二叉树的链式 存储表示,一、二叉树的顺序 存储表示,#define MAX_TREE_SIZE 100/二叉树的最大结点数typedef TElemType SqBiTreeMAX_TREE_SIZE;/0号单元存储根结点SqBiTree bt;,一、二叉树的顺序存储
7、表示,例如:,1,4,0,13,2,6,二、二叉树的链式存储表示,1.二叉链表,2三叉链表,3双亲链表,4线索链表,root,结点结构:,1.二叉链表,typedef struct BiTNode/结点结构 TElemType data;struct BiTNode*lchild,*rchild;/左右孩子指针 BiTNode,*BiTree;,结点结构:,C 语言的类型描述如下:,root,2三叉链表,结点结构:,typedef struct TriTNode/结点结构 TElemType data;struct TriTNode*lchild,*rchild;/左右孩子指针 struct
8、TriTNode*parent;/双亲指针 TriTNode,*TriTree;,结点结构:,C 语言的类型描述如下:,结点结构:,3双亲链表,LRTag,LRRRRL,typedef struct BPTNode/结点结构 TElemType data;int*parent;/指向双亲的指针 char LRTag;/左、右孩子标志域 BPTNode typedef struct BPTree/树结构 BPTNode nodesMAX_TREE_SIZE;int num_node;/结点数目 int root;/根结点的位置 BPTree,二叉树的主要基本操作:,(1)Init(bt)构造一棵
9、空二叉树、(2)Create(x,lbt,rbt)生成一棵以x为根结点,以二叉树lbt和rbt为左子树和右子树的二叉树(3)InsertL(x,Parent)将值为x的结点插入到二叉树中结点Parent的左孩子位置。如果结点Parent原来有左孩子结点,则将结点Parent原来的左孩子结点作为结点x的左孩子结点。(4)InsertR(x,Parent)将值为x的结点插入到二叉树中结点Parent的右孩子位置。如果结点Parent原来有右孩子结点,则将结点Parent原来的右孩子结点作为结点x的右孩子结点。,(5)DeleteL(bt,Parent)在二叉树bt中删除结点Parent的左子树(6
10、)DeleteR(bt,Parent)在二叉树bt中删除结点Parent的右子树(7)Search(bt,x)在二叉树bt中查找数据元素x(8)Traverse(bt)按某种方式遍历二叉树,实现算法,(1)Init(bt)int Init(BiTree bt)if(bt=(BiTree)malloc(sizeof(BiTNode)=NULL)return 0;bt-Lchild=NULL;bt-rchild=NULL;return 1;,(2)Create(x,lbt,rbt)BiTree Create(x,lbt,rbt)BiTree p;if(bt=(BiTree)malloc(sizeo
11、f(BiTNode)=NULL)return NULL;p-data=x;p-lchild=lbt;p-rchild=rbt;return p;,(3)InsertL(x,Parent)BiTree InsertL(x,Parent)BiTree p;if(Parent=NULL)return NULL;if(bt=(BiTree)malloc(sizeof(BiTNode)=NULL)return NULL;p-data=x;p-lchild=NULL;p-rchild=NULL;if(Parent-lchild=NULL)Parent-lchild=p;else p-lchild=Pare
12、nt-lchild;Parent-lchild=p;,(5)DeleteL(bt,Parent)BiTree DeleteL(bt,Parent)BiTree p;if(Parent=NULL|Parent-lchild=NULL)return NULL;p=Parent-lchild;Parent-lchild=NULL;free(p);return bt;,6.4二叉树的遍历,一、问题的提出,二、先左后右的遍历算法,三、算法的递归描述,四、中序遍历算法的非递归描述,四、遍历算法的应用举例,顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。,一、问题的提出,“
13、访问”的含义可以很广,如:输出结点的信息等。,“遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。而二叉树是非线性结构,,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径进行遍历的问题。,对“二叉树”而言,可以有三条搜索路径:,1先上后下的按层次遍历;2先左(子树)后右(子树)的遍历;3先右(子树)后左(子树)的遍历。,二、先左后右的遍历算法,先(根)序的遍历算法,中(根)序的遍历算法,后(根)序的遍历算法,根,左子树,右子树,根,根,根,根,根,若二叉树为空树,则空操作;否则,(1)访问根结点;(2)先序遍历左子树;(3)先序
14、遍历右子树。,先(根)序的遍历算法:,若二叉树为空树,则空操作;否则,(1)中序遍历左子树;(2)访问根结点;(3)中序遍历右子树。,中(根)序的遍历算法:,若二叉树为空树,则空操作;否则,(1)后序遍历左子树;(2)后序遍历右子树;(3)访问根结点。,后(根)序的遍历算法:,例如:,先序序列:,中序序列:,后序序列:,A B C D E F G H K,B D C A E H G K F,D C B H K G F E A,三、算法的递归描述,void Preorder(BiTree T)/先序遍历二叉树 if(T)printf(“%d”,T-data);/访问结点 Preorder(T-l
15、child,visit);/遍历左子树 Preorder(T-rchild,visit);/遍历右子树,前序遍历算法的非递归描述,void preorder(BiTree bt)BiTree*stackMaxsize,p;int top;if(bt!=NULL)top=1;stacktop=b;/根结点入栈 while(top0)/栈不为空时循环 p=stacktop-;/退栈并访问该结点 printf(“%d”,p-data);if(p-rchild!=NULL)/右孩子入栈 stacktop=p-rchild;top+;if(p-lchild!=NULL)/左孩子入栈 stacktop=p
16、-lchild;top+;/while/if,中序遍历算法的非递归描述,BiTNode*GoFarLeft(BiTree T,Stack*S)if(!T)return NULL;while(T-lchild)Push(S,T);T=T-lchild;return T;,void Inorder(BiTree T,void(*visit)(TelemType/栈空表明遍历结束/while,t=GoFarLeft(t-rchild,S);,四、遍历算法的应用举例,2、统计二叉树中叶子结点的个数,3、求二叉树的深度(后序遍历),4、建立二叉树的存储结构,1、查询二叉树中某个结点,1.在二叉树不空的前
17、提下,和根结点的元素进行比较,若相等,则找到返回 TRUE;,2.否则在左子树中进行查找,若找到,则返回 TRUE;,3.否则继续在右子树中进行查找,若找到,则返回 TRUE,否则返回 FALSE;,Status Preorder(BiTree T,ElemType x)/若二叉树中存在和 x 相同的元素,则 p 指向该结点并返回 OK,/否则返回 FALSE,if(T)if(T-data=x)p=T;return OK,/if else return FALSE;,else if(Preorder(T-lchild,x)return OK;else return(Preorder(T-rch
18、ild,x);/else,2、统计二叉树中叶子结点的个数,int count=0;int CountLeaf(BiTree T,count)if(T)if(!T-lchild)/CountLeaf,3、求二叉树的深度(后序遍历),算法基本思想:,从二叉树深度的定义可知,二叉树的深度应为其左、右子树深度的最大值加1。由此,需先分别求得左、右子树的深度,算法中“访问结点”的操作为:求得左、右子树深度的最大值,然后加 1。,首先分析二叉树的深度和它的左、右子树深度之间的关系。,int Depth(BiTree T)/返回二叉树的深度 if(!T)depthval=0;else depthLeft=D
19、epth(T-lchild);depthRight=Depth(T-rchild);depthval=1+(depthLeft depthRight?depthLeft:depthRight);return depthval;,5、建立二叉树的存储结构,不同的定义方法相应有不同的存储结构的建立算法,以字符串的形式“根 左子树 右子树”定义一棵二叉树,例如:,以空白字符“”表示,AB C D,空树,只含一个根结点的二叉树,A,以字符串“A”表示,以下列字符串表示,int CreateBiTree(BiTree/CreateBiTree,A B C D,A,B,C,D,上页算法执行过程举例如下:,
20、A,T,B,C,D,scanf(,仅知二叉树的先序序列“abcdefg”能否唯一确定一棵二叉树?,由二叉树的先序和中序序列建树,如果同时已知二叉树的中序序列“cbdaegf”,则会如何?,二叉树的先序序列,二叉树的中序序列,左子树,左子树,右子树,右子树,根,根,a b c d e f g,c b d a e g f,例如:,a,a,b,b,c,c,d,d,e,e,f,f,g,g,a,b,c,d,e,f,g,先序序列中序序列,6.5线索二叉树,何谓线索二叉树?线索链表的遍历算法,一、何谓线索二叉树?,遍历二叉树的结果是,求得结点的一个线性序列。,A,B,C,D,E,F,G,H,K,例如:,先序
21、序列: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,指向该线性序列中的“前驱”和“后继”的指针,称作“线索”,与其相应的二叉树,称作“线索二叉树”,包含“线索”的存储结构,称作“线索链表”,A B C D E F G H K,D,C,B,E,对线索链表中结点的约定:,在二叉链表的结点中增加两个标志域,并作如下规定:,若该结点的左子树不空,则Lchild域的指针指向其左子树,且左标志域的值为“0”;否则,Lchild域的指针指向其“前驱”,且左标志的值为“1”。,若该结点的右子树不空,则rchild域的指针指向其右
22、子树,且右标志域的值为“0”;否则,rchild域的指针指向其“后继”,且右标志的值为“1”。,如此定义的二叉树的存储结构称作“线索链表”,typedef struct BiThrNod TElemType data;struct BiThrNode*lchild,*rchild;/左右指针 PointerThr LTag,RTag;/左右标志 BiThrNode,*BiThrTree;,线索链表的类型描述:,typedef enum Link,Thread PointerThr;/Link=0:指针,Thread=1:线索,二、线索链表的遍历算法:,for(p=firstNode(T);p;
23、p=Succ(p)Visit(p);,由于在线索链表中添加了遍历中得到的“前驱”和“后继”的信息,从而简化了遍历的算法。,例如:对中序线索化链表的遍历算法,中序遍历的第一个结点?,在中序线索化链表中结点的后继?,左子树上处于“最左下”(没有左子树)的结点,若无右子树,则为后继线索所指结点,否则为对其右子树进行中序遍历时访问的第一个结点,void InOrderTraverse_Thr(BiThrTree T)p=T-lchild;/p指向根结点 while(p!=T)/空树或遍历结束时,p=T while(p-LTag=Link)p=p-lchild;visit(p-data)/第一个结点 w
24、hile(p-RTag=Thread/p进至其右子树根/InOrderTraverse_Thr,6.6 树和森林 的表示方法,树的三种存储结构,一、双亲表示法,二、孩子链表表示法,三、树的二叉链表(孩子-兄弟)存储表示法,r=0n=7,data parent,一、双亲表示法:,typedef struct PTNode Elem data;int parent;/双亲位置域 PTNode;,#define MAX_TREE_SIZE 100,结点结构:,C语言的类型描述:,typedef struct PTNode nodes MAX_TREE_SIZE;int r,n;/根结点的位置和结点个
25、数 PTree;,树结构:,r=0n=7,data firstchild,二、孩子链表表示法:,-1 0 0 0 2 2 4,typedef struct CTNode int child;struct CTNode*nextchild;*ChildPtr;,孩子结点结构:,C语言的类型描述:,typedef struct Elem data;ChildPtr firstchild;/孩子链的头指针 CTBox;,双亲结点结构,typedef struct CTBox nodesMAX_TREE_SIZE;int n,r;/结点数和根结点的位置 CTree;,树结构:,root,三、树的二叉链
26、表(孩子-兄弟)存储表示法,root,typedef struct CSNode Elem data;struct CSNode*firstchild,*nextsibling;CSNode,*CSTree;,C语言的类型描述:,结点结构:,森林和二叉树的对应关系,设森林 F=(T1,T2,Tn);T1=(root,t11,t12,t1m);,二叉树 B=(LBT,Node(root),RBT);,由森林转换成二叉树的转换规则为:,若 F=,则 B=;,由 ROOT(T1)对应得到Node(root);,否则,,由(t11,t12,t1m)对应得到 LBT;,由(T2,T3,Tn)对应得到 R
27、BT。,由二叉树转换为森林的转换规则为:,由LBT 对应得到(t11,t12,,t1m);,若 B=,则 F=;,否则,,由 Node(root)对应得到 ROOT(T1);,由RBT 对应得到(T2,T3,Tn)。,T1,T2,Tn,由此,树和森林的各种操作均可与二叉树的各种操作相对应。,应当注意的是,和树对应的二叉树,其左、右子树的概念已改变为:左是孩子,右是兄弟,6.7树和森林的遍历,一、树的遍历,二、森林的遍历,三、树的遍历的应用,树的遍历可有3条搜索路径:,按层次遍历:,先根(次序)遍历:,后根(次序)遍历:,若树不空,则先访问根结点,然后依次先根遍历各棵子树。,若树不空,则先依次后
28、根遍历各棵子树,然后访问根结点。,若树不空,则自上而下自左至右访问树中每个结点。,层次遍历时顶点的访问次序:,先根遍历时顶点的访问次序:,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,1。森林中第一棵树的根结点;,2。森林中第一棵树的子树森林;,3。森林中其它树构成的森林。,可以分解成三部分:,森林,若森林不空,则访问森林中第一棵树的根结点;先序遍历森林中第一棵树的子树森林;先序遍历森林中(除第一棵树之外)其 余树构成的森林。,先序遍历,森林的遍历,即:依次从左至右对森林中的每一棵
29、树进行先根遍历。,中序遍历,若森林不空,则中序遍历森林中第一棵树的子树森林;访问森林中第一棵树的根结点;中序遍历森林中(除第一棵树之外)其 余树构成的森林。,即:依次从左至右对森林中的每一棵树进行后根遍历。,树的遍历和二叉树遍历的对应关系?,先根遍历,后根遍历,树,二叉树,森林,先序遍历,先序遍历,中序遍历,中序遍历,设树的存储结构为孩子兄弟链表,typedef struct CSNode Elem data;struct CSNode*firstchild,*nextsibling;CSNode,*CSTree;,求树的深度,Int Depth(CSTree T)If(T=NULL)retu
30、rn 0;ElseD1=Depth(T-firstchild);D2=Depth(T-nextsibling);Return Maxd1+1,d2,int TreeDepth(CTree T)/T 是树的孩子链表存储结构,/返回该树的深度 if(T.n=0)return 0;else return Depth(T,T.r);/TreeDepth,一、求树的深度的算法:,int Depth(CTree T,int root)max=0;p=T.nodesroot.firstchild;while(p)h=Depth(T,p-child);if(h max)max=h;p=p-nextchild;
31、/while return max+1;,6.8 哈 夫 曼 树 与 哈 夫 曼 编 码,最优树的定义 如何构造最优树 前缀编码,一、最优树的定义,树的路径长度定义为:树中每个结点的路径长度之和。,结点的路径长度定义为:从根结点到该结点的路径上 分支的数目。,树的带权路径长度定义为:树中所有叶子结点的带权路径长度之和 WPL(T)=wklk(对所有叶子结点),在所有含 n 个叶子结点、并带相同权值的二叉树中,必存在一棵其带权路径长度取最小值的树,称为“最优树”。,例如:,2,7 9,7,5,4,9,2,WPL(T)=72+52+23+43+92=60,WPL(T)=74+94+53+42+21
32、=89,5,4,最佳判定树,考试成绩分布表,判定树,不及格,及格,中,良,优,60?,70?,80?,90?,0.10,0.15,0.25,0.35,0.15,WPL=0.10*1+0.15*2+0.25*3+0.35*4+0.15*4=3.15,最佳判定树,不及格,及格,中,良,优,60?,70?,80?,90?,0.10,0.15,0.25,0.35,0.15,WPL=0.10*3+0.15*3+0.25*2+0.35*2+0.15*2=0.3+0.45+0.5+0.7+0.3=2.25,根据给定的 n 个权值 w1,w2,wn,构造 n 棵二叉树的集合 F=T1,T2,Tn,其中每棵二叉
33、树中均只含一个带权值 为 wi 的根结点,其左、右子树为空树;,二、如何构造最优树,(1),(赫夫曼算法)以二叉树为例:,在 F 中选取其根结点的权值为最 小的两棵二叉树,分别作为左、右子树构造一棵新的二叉树,并 置这棵新的二叉树根结点的权值 为其左、右子树根结点的权值之 和;,(2),从F中删去这两棵树,同时加入 刚生成的新树;,重复(2)和(3)两步,直至 F 中只 含一棵树为止。,(3),(4),9,例如:已知权值 W=5,6,2,9,7,5,6,2,7,5,2,7,6,9,7,6,7,13,9,9,9,16,29,0,0,0,0,1,1,1,1,00,01,11,100,101,指的是
34、,任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀。,三、前缀编码,利用赫夫曼树可以构造一种不等长的二进制编码,并且构造所得的赫夫曼编码是一种最优前缀编码,即使所传电文的总长度最短。,赫夫曼编码,设给出一段报文:CAST CAST SAT AT A TASA 字符集合是 C,A,S,T,各个字符出现的频度(次数)是 W 2,7,4,5。若给每个字符以等长编码 A:00 T:10 C:01 S:11则总编码长度为(2+7+4+5)*2=36.,若按各个字符出现的概率不同而给予不等长编码,可望减少总编码长度。各字符出现概率为 2/18,7/18,4/18,5/18,化整为 2,7,4,5
35、。以它们为各叶结点上的权值,建立霍夫曼树。左分支赋 0,右分支赋 1,得霍夫曼编码(变长编码)。,7,2,5,4,0,1,0,0,1,1,A,C,T,S,A:0 T:10 C:110 S:111它的总编码长度:7*1+5*2+(2+4)*3=35。比等长编码的情形要短。总编码长度正好等于霍夫曼树的带权路径长度WPL。霍夫曼编码是一种无前缀编码。解码时不会混淆。,霍夫曼编码树,1.熟练掌握二叉树的结构特性,了解相应的证明方法。2.熟悉二叉树的各种存储结构的特点及适用范围。3.遍历二叉树是二叉树各种操作的基础。实现二叉树遍历的具体算法与所采用的存储结构有关。掌握各种遍历策略的递归算法,灵活运用遍历
36、算法实现二叉树的其它操作。层次遍历是按另一种搜索策略进行的遍历。,4.理解二叉树线索化的实质是建立结点与其在相应序列中的前驱或后继之间的直接联系,熟练掌握二叉树的线索化过程以及在中序线索化树上找给定结点的前驱和后继的方法。二叉树的线索化过程是基于对二叉树进行遍历,而线索二叉树上的线索又为相应的遍历提供了方便。,5.熟悉树的各种存储结构及其特点,掌握树和森林与二叉树的转换方法。建立存储结构是进行其它操作的前提,因此应掌握 1 至 2 种建立二叉树的存储结构的方法。6.了解最优树的特性,掌握建立最优树和哈夫曼编码的方法。,作业,1、由3个结点可以构造出多少种不同的二叉树?画出它们。2、一棵高度为k
37、的完全二叉树至少有多少个结点?至多呢?3、一棵完全二叉树上有100个结点,其中叶子结点有多少个?写出分析过程。4、已知一棵二叉树的后序遍历序列为LGHDIEBJKFCA,中序遍历序列为GLDHBEIACJFK,画出这棵二叉树,并写出后序遍历序列。,5、若在内存中存放一个完全二叉树,在二叉树上只进行下面2个操作:1)寻找某个结点双亲;2)寻找某个结点的孩子。问:应该用何种存储结构来存储该二叉树?在该存贮结构下如何找到某结点的双亲和孩子结点?6、求含有n个结点、采用顺序存储结构的完全二叉树中的序号最小的叶子结点的下标。要求写出简要步骤。7、编写递归算法,在二叉树中求位于先序序列中第k个位置的结点的值。,int c=0;void getPreSeq(Bitree T,int k)if(T)c+;if(c=k)printf(“value is%cn”,T-data);exit(1);else getPreSeq(T-lchild,int k);getPreSeq(T-rchild,int k);,