
数据结构严蔚敏第6章课件.ppt
141页第六章第六章树和二叉树树和二叉树数据结构严蔚敏第6章教学目的和要求教学目的和要求§1、熟练掌握二叉树的结构特点,了解相应的证明熟练掌握二叉树的结构特点,了解相应的证明§2、熟悉二叉树的各种存储结构的特点及适用范围熟悉二叉树的各种存储结构的特点及适用范围§3、掌握二叉树遍历的递归与非递归算法掌握二叉树遍历的递归与非递归算法§4、掌握二叉线索树的相关算法掌握二叉线索树的相关算法§5、熟悉树的各种存储结构及特点,掌握树和森林、熟悉树的各种存储结构及特点,掌握树和森林与二叉树的方法与二叉树的方法§6、了解最优树的特性,掌握最优树和哈夫曼编码、了解最优树的特性,掌握最优树和哈夫曼编码的方法数据结构严蔚敏第6章 1.数据的逻辑结构 2、数据的存储结构 3、数据的运算:检索、排序、插入、删除、修改等 A.线性结构 B.非线性结构A 顺序存储 B 链式存储 线性表栈队树形结构图形结构数据结构的三个主要问题 数据结构严蔚敏第6章树形结构树形结构全校学生档案管理的组织方式全校学生档案管理的组织方式数据结构严蔚敏第6章ABCDEFGH树形结构树形结构 —— 结点间具有分层次的连接关系结点间具有分层次的连接关系HBCDEFGA数据结构严蔚敏第6章6.1 树的类型定义树的类型定义6.2 6.2 二叉树的类型定义二叉树的类型定义6.3 二叉树的存储结构二叉树的存储结构6.4 二叉树的遍历二叉树的遍历6.5 线索二叉树线索二叉树6.6 树和森林的表示方法树和森林的表示方法6.7 树和森林的遍历树和森林的遍历6.8 哈夫曼树与哈夫曼编码哈夫曼树与哈夫曼编码数据结构严蔚敏第6章6.1 树的类型定义树的类型定义数据结构严蔚敏第6章数据对象数据对象 D:D是具有相同特性的数据元素的集合。
是具有相同特性的数据元素的集合 若若D为空集,则称为空树为空集,则称为空树 否则否则: (1) 在在D中存在唯一的称为根的数据元素中存在唯一的称为根的数据元素root;; (2) 当当n>1时,其余结点可分为时,其余结点可分为m (m>0)个互个互 不相交的有限集不相交的有限集T1, T2, ……, Tm,其中每一,其中每一 棵子集本身又是一棵符合本定义的树,棵子集本身又是一棵符合本定义的树, 称为根称为根root的子树 数据关系数据关系 R:数据结构严蔚敏第6章ABCDEFGHIJMKLA( B(E, F(K, L)), C(G), D(H, I, J(M)) )T1T3T2树根例如例如: :数据结构严蔚敏第6章 基本操作:基本操作:查查 找找 类类 插插 入入 类类删删 除除 类类数据结构严蔚敏第6章 Root(T) // 求树的根结点求树的根结点 查找类:查找类:Value(T, cur_e) // 求当前结点的元素值求当前结点的元素值 Parent(T, cur_e) // 求当前结点的双亲结点求当前结点的双亲结点LeftChild(T, cur_e) // 求当前结点的最左孩子求当前结点的最左孩子 RightSibling(T, cur_e) // 求当前结点的右兄弟求当前结点的右兄弟TreeEmpty(T) // 判定树是否为空树判定树是否为空树 TreeDepth(T) // 求树的深度求树的深度TraverseTree( T, Visit() ) // 遍历遍历数据结构严蔚敏第6章InitTree(&T) // 初始化置空树初始化置空树 插入类:插入类:CreateTree(&T, definition) // 按定义构造树按定义构造树Assign(T, cur_e, value) // 给当前结点赋值给当前结点赋值InsertChild(&T, &p, i, c) // 将以将以c为根的树插入为结点为根的树插入为结点p的第的第i棵子树棵子树数据结构严蔚敏第6章 ClearTree(&T) // 将树清空将树清空 删除类:删除类:DestroyTree(&T) // 销毁树的结构销毁树的结构DeleteChild(&T, &p, i) // 删除结点删除结点p的第的第i棵子树棵子树数据结构严蔚敏第6章对比对比树型结构树型结构和和线性结构线性结构的结构特点的结构特点数据结构严蔚敏第6章线性结构线性结构树型结构树型结构第一个数据元素第一个数据元素 ( (无前驱无前驱) ) 根结点根结点 ( (无前驱无前驱) )最后一个数据元素最后一个数据元素 (无后继无后继)多个叶子结点多个叶子结点 ( (无后继无后继) )其它数据元素其它数据元素( (一个前驱、一个前驱、 一个后继一个后继) )其它数据元素其它数据元素( (一个前驱、一个前驱、 多个后继多个后继) )数据结构严蔚敏第6章基基 本本 术术 语语数据结构严蔚敏第6章结点结点: :结点的度结点的度: :树的度树的度: :叶子结点叶子结点: :分支结点分支结点: :数据元素+ +若干指向子树的分支分支的个数树中所有结点的度的最大值度为零的结点度大于零的结点DHIJM数据结构严蔚敏第6章(从根到结点的)路径路径::孩子孩子结点、双亲双亲结点兄弟兄弟结点、堂兄弟祖先祖先结点、子孙子孙结点结点的层次结点的层次: :树的深度:树的深度: 由从根根到该结点所经分支和结点构成ABCDEFGHIJMKL假设根结点的层次为1,第l 层的结点的子树根结点的层次为l+1树中叶子结点所在的最大层次数据结构严蔚敏第6章任何一棵非空树是一个二元组 Tree = ((root,,F))其中:root 被称为根结点 F 被称为子树森林森林:森林:是m(m≥0)棵互不相交的树的集合ArootBCDEFGHIJMKLF数据结构严蔚敏第6章6.2 二叉树的类型定义二叉树的类型定义数据结构严蔚敏第6章 二叉树或为空树空树,或是由一个根结根结点点加上两棵两棵分别称为左子树左子树和右子树的、互不交的互不交的二叉树二叉树组成。
ABCDEFGHK根结点左子树右子树数据结构严蔚敏第6章二叉树的五种基本形态:二叉树的五种基本形态:N空树空树只含根结点只含根结点NNNLRR右子树为空树右子树为空树L左子树为空树左子树为空树左右子左右子树均不树均不为空树为空树数据结构严蔚敏第6章 二叉树的主要基本操作二叉树的主要基本操作:查查 找找 类类插插 入入 类类删删 除除 类类数据结构严蔚敏第6章 Root(T); Value(T, e); Parent(T, e); LeftChild(T, e); RightChild(T, e); LeftSibling(T, e); RightSibling(T, e); BiTreeEmpty(T); BiTreeDepth(T); PreOrderTraverse(T, Visit()); InOrderTraverse(T, Visit()); PostOrderTraverse(T, Visit()); LevelOrderTraverse(T, Visit());数据结构严蔚敏第6章 InitBiTree(&T); Assign(T, &e, value); CreateBiTree(&T, definition); InsertChild(T, p, LR, c);数据结构严蔚敏第6章ClearBiTree(&T); DestroyBiTree(&T);DeleteChild(T, p, LR);数据结构严蔚敏第6章二叉树二叉树的重要特性的重要特性数据结构严蔚敏第6章§ 性质性质 1 :: 在二叉树的第 i 层上至多有2i-1 个结点。
(i≥1)用归纳法证明用归纳法证明:: 归纳基归纳基:: 归纳假设:归纳假设: 归纳证明:归纳证明:i = 1 层时,只有一个根结点: 2i-1 = 20 = 1;假设对所有的 j,1≤ j i,命题成立;二叉树上每个结点至多有两棵子树,则第 i 层的结点数 = 2i-2 2 = 2i-1 数据结构严蔚敏第6章§性质性质 2 :: 深度为 k 的二叉树上至多含 2k-1 个结点(k≥1)证明:证明: 基于上一条性质,深度为 k 的二叉树上的结点数至多为 20+21+ +2k-1 = 2k-1 数据结构严蔚敏第6章§ 性质性质 3 :: 对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为 2 的结点,则必存在关系式:n0 = n2+1证明:证明:设设 二叉树上结点总数 n = n0 + n1 + n2又又 二叉树上分支总数 b = n1+2n2 而 b = n-1 = n0 + n1 + n2 - 1由此,由此, n0 = n2 + 1 。
数据结构严蔚敏第6章两类两类特殊特殊的二叉树:的二叉树:满二叉树满二叉树:指的是深度为k且含有2k-1个结点的二叉树完全二叉树完全二叉树:树中所含的 n 个结点和满二叉树中编号编号为为 1 至至 n 的结点的结点一一对应123456789 10 11 12 13 14 15abcdefghij数据结构严蔚敏第6章§ 性质性质 4 :: 具有 n 个结点的完全二叉树的深深度度为 log2n +1 证明:证明:设设完全二叉树的深度为 k 则根据第二条性质得 2k-1≤ n < 2k 即 k-1 ≤ log2 n < k 因为因为 k 只能是整数,因此,只能是整数,因此, k = log2n + 1 数据结构严蔚敏第6章§性质性质 5 ::若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:(1) 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 i/2 的结点为其双亲双亲结点;(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子左孩子结点;(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子右孩子结点。
数据结构严蔚敏第6章6.3 二叉树的存储结构二叉树的存储结构二、二叉树的链式二、二叉树的链式 存储表示存储表示一、一、 二叉树的顺序二叉树的顺序 存储表示存储表示数据结构严蔚敏第6章#define MAX_TREE_SIZE 100 // 二叉树的最大结点数typedef TElemType SqBiTree[MAX_ TREE_SIZE]; // 0号单元存储根结点SqBiTree bt;一、一、 二叉树的顺序存储表示二叉树的顺序存储表示数据结构严蔚敏第6章例如例如:ABCDEF A B D C E F 0 1 2 3 4 5 6 7 8 9 10 11 12 131401326顺序存储结构仅适用于完全二叉树!顺序存储结构仅适用于完全二叉树!数据结构严蔚敏第6章二、二叉树的链式存储表示二、二叉树的链式存储表示1. 1. 二叉链表二叉链表2..三叉链表三叉链表3 3.双亲链表.双亲链表4.线索链表.线索链表数据结构严蔚敏第6章ADEBCF rootlchild data rchild结点结构结点结构:1. 1. 二叉链表二叉链表数据结构严蔚敏第6章typedef struct BiTNode { // 结点结构结点结构 TElemType data; struct BiTNode *lchild, *rchild; // 左右孩子指针} BiTNode, *BiTree;lchild data rchild结点结构结点结构:C 语言的类型描述如下语言的类型描述如下: :数据结构严蔚敏第6章ADEBCF root 2..三叉链表三叉链表parent lchild data rchild结点结构结点结构:数据结构严蔚敏第6章 typedef struct TriTNode { // 结点结构结点结构 TElemType data; struct TriTNode *lchild, *rchild; // 左右孩子指针 struct TriTNode *parent; //双亲指针 } TriTNode, *TriTree;parent lchild data rchild结点结构结点结构:C 语言的类型描述如下语言的类型描述如下: :数据结构严蔚敏第6章0123456 data parent结点结构结点结构:3 3.双亲链表.双亲链表LRTagLRRRL数据结构严蔚敏第6章 typedef struct BPTNode { // 结点结构结点结构 TElemType data; int *parent; // 指向双亲的指针 char LRTag; // 左、右孩子标志域 } BPTNode typedef struct BPTree{ // 树结构树结构 BPTNode nodes[MAX_TREE_SIZE]; int num_node; // 结点数目 int root; // 根结点的位置 } BPTree数据结构严蔚敏第6章6.4二叉树的遍历二叉树的遍历数据结构严蔚敏第6章一、问题的提出一、问题的提出二、先左后右的遍历算法二、先左后右的遍历算法三、算法的递归描述三、算法的递归描述四、中序遍历算法的非递归描述四、中序遍历算法的非递归描述五五、遍历算法的应用举例遍历算法的应用举例数据结构严蔚敏第6章 顺着某一条搜索路径巡访巡访二叉树中的结点,使得每个结点均被访问一均被访问一次次,而且仅被访问一次仅被访问一次。
一、问题的提出一、问题的提出“访问访问”的含义可以很广,如:输出结点的信息等数据结构严蔚敏第6章 “遍历遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论而二叉树是非线性结构, 每个结点有两个后继每个结点有两个后继,则存在如何遍历存在如何遍历即按什么样的搜索搜索路径路径遍历的问题数据结构严蔚敏第6章 对对“二二叉叉树树”而而言言,,可可以以有有三条搜索路径:三条搜索路径:§1.先上后下先上后下的按层次遍历;§2.先先左左(子树)后后右右(子树)的遍历;§3.先先右右(子树)后后左左(子树)的遍历数据结构严蔚敏第6章二、先左后右的遍历算法二、先左后右的遍历算法先先序(根)的遍历算法中中序(根)的遍历算法后后序(根)的遍历算法数据结构严蔚敏第6章 若二叉树为空树,则空操作;否则,(1)访问根结点;(2)先序遍历左子树;(3)先序遍历右子树先序(根)的遍历算法:先序(根)的遍历算法:数据结构严蔚敏第6章 若二叉树为空树,则空操作;否则,(1)中序遍历左子树;(2)访问根结点;(3)中序遍历右子树。
中序(根)的遍历算法:中序(根)的遍历算法:数据结构严蔚敏第6章 若二叉树为空树,则空操作;否则,(1)后序遍历左子树;(2)后序遍历右子树;(3)访问根结点后序(根)的遍历算法:后序(根)的遍历算法:数据结构严蔚敏第6章课堂提问:§有以下结构的二叉树有以下结构的二叉树§写出其先序、中序和后序遍历的序列写出其先序、中序和后序遍历的序列ABCDE数据结构严蔚敏第6章三、算法的递归描述三、算法的递归描述void Preorder (BiTree T, void( *visit)(TElemType& e)){ { // 先序遍历二叉树 if (T) { visit(T->data); // 访问结点 Preorder(T->lchild, visit); // 遍历左子树 Preorder(T->rchild, visit);// 遍历右子树 } }} }数据结构严蔚敏第6章四、中序遍历算法的非递归描述四、中序遍历算法的非递归描述BiTNode *GoFarLeft(BiTree T, Stack *S){ if (!T ) return NULL; while (T->lchild ){ { Push(S, T); T = T->lchild; } } return T;} }数据结构严蔚敏第6章void Inorder_I(BiTree T, void (*visit) (TelemType& e)){ Stack *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 数据结构严蔚敏第6章五五、遍历算法的应用举例遍历算法的应用举例1、统计二叉树中叶子结点的个数、统计二叉树中叶子结点的个数 (先序遍历先序遍历)2、求二叉树的深度、求二叉树的深度(后序遍历后序遍历)3、复制二叉树、复制二叉树(后序遍历后序遍历)4 4、建立二叉树的存储结构、建立二叉树的存储结构数据结构严蔚敏第6章1、统计二叉树中叶子结点的个数、统计二叉树中叶子结点的个数算法基本思想算法基本思想: : 先序(或中序或后序)遍历二叉树,在遍历过程中查找叶子结点,并计数。
由此,需在遍历算法中增添一个需在遍历算法中增添一个““计数计数””的参数,的参数,并将算法中“访问结点”的操作改为:若是叶子,则计数器增若是叶子,则计数器增1 1数据结构严蔚敏第6章void CountLeaf (BiTree T, int& count){ if ( T ) { if ((!T->lchild)&& (!T->rchild)) count++; // 对叶子结点计数 CountLeaf( T->lchild, count); CountLeaf( T->rchild, count); } // if} // CountLeaf数据结构严蔚敏第6章2、求二叉树的深度、求二叉树的深度(后序遍历后序遍历)算法基本思想算法基本思想: : 从二叉树深度的定义可知,二叉树的二叉树的深度应为其左、右子树深度的最大值加深度应为其左、右子树深度的最大值加1 1由此,需先分别求得左、右子树的深度,需先分别求得左、右子树的深度,算法中“访问结点”的操作为:求得左、求得左、右子树深度的最大值,然后加右子树深度的最大值,然后加 1 1 。
首先分析二叉树的深度二叉树的深度和它的左左、右子右子树深度树深度之间的关系数据结构严蔚敏第6章int Depth (BiTree T ){ // 返回二叉树的深度 if ( !T ) depthval = 0; else { depthLeft = Depth( T->lchild ); depthRight= Depth( T->rchild ); depthval = 1 + (depthLeft > depthRight ? depthLeft : depthRight); } return depthval;}数据结构严蔚敏第6章3、复制二叉树、复制二叉树其基本操作为:生成一个结点其基本操作为:生成一个结点根元素根元素T左子树左子树右子树右子树根元素根元素NEWT左子树左子树右子树右子树左子树左子树右子树右子树(后序遍历后序遍历)数据结构严蔚敏第6章BiTNode *GetTreeNode(TElemType item, BiTNode *lptr , BiTNode *rptr ){ if (!(T = (BiTNode*)malloc(sizeof(BiTNode)))) exit(OVERFLOW); T-> data = item; T-> lchild = lptr; T-> rchild = rptr; return T;} 生成一个二叉树的结点生成一个二叉树的结点(其数据域为其数据域为item,左指针域为左指针域为lptr,右指针域为右指针域为rptr)数据结构严蔚敏第6章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 newrptr = NULL; newT = GetTreeNode(T->data, newlptr, newrptr); return newT;} // CopyTree数据结构严蔚敏第6章ABCDEFGHK^ D ^ C ^^ B ^ H ^^ K ^G ^ F ^ E ^ A例如:下列二叉树例如:下列二叉树的复制过程如下:的复制过程如下:newT数据结构严蔚敏第6章4 4、建立二叉树的存储、建立二叉树的存储结构结构不同的定义方法相应有不同的不同的定义方法相应有不同的存储结构的建立算法存储结构的建立算法数据结构严蔚敏第6章以字符串“A!!”表示 以字符串的形式以字符串的形式 根根 左子树左子树 右子树右子树定义一棵二叉树定义一棵二叉树例如:ABCD以字符“!”表示A(B(! ,C(! , ! )),D(! , ! ))空树空树只含一个根结点只含一个根结点的二叉树的二叉树A以下列字符串表示数据结构严蔚敏第6章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数据结构严蔚敏第6章 仅知二叉树的先序序列“abcdefg” 不能唯一确定一棵二叉树,由二叉树的先序和中序序列建树由二叉树的先序和中序序列建树 如果同时已知二叉树的中序序列“cbdaegf”,则会如何? 二叉树的先序序列二叉树的中序序列左子树左子树左子树左子树 右子树右子树右子树右子树根根根根数据结构严蔚敏第6章a b c d e f gc b d a e g f例如例如: :aab bccddeeffggabcdefg^^^^^^^^先序序列中序序列数据结构严蔚敏第6章void CrtBT(BiTree& T, char pre[], char ino[], int ps, int is, int n ) { // 已知pre[ps..ps+n-1]为二叉树的先序序列, // ins[is..is+n-1]为二叉树的中序序列,本算 // 法由此两个序列构造二叉链表 if (n==0) T=NULL; else { k=Search(ino, pre[ps]); // 在中序序列中查询 if (k== -1) T=NULL; else { } } //} // CrtBT … … 数据结构严蔚敏第6章T=(BiTNode*)malloc(sizeof(BiTNode));T->data = pre[ps];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 );数据结构严蔚敏第6章练习题:练习题:§1、编写递归算法,将二叉树中所有结点、编写递归算法,将二叉树中所有结点的左、右子树交换。
的左、右子树交换§2、编写递归算法:对于二叉树中每一个、编写递归算法:对于二叉树中每一个元素值为元素值为x的结点,删除以它为根的子树,的结点,删除以它为根的子树,并释放相应的空间并释放相应的空间§3、编写按层次顺序(同一层自左至右)、编写按层次顺序(同一层自左至右)遍历二叉树的算法遍历二叉树的算法数据结构严蔚敏第6章6.5线索二叉树线索二叉树§ 何谓线索二叉树?何谓线索二叉树?§ 线索链表的遍历算法线索链表的遍历算法§ 如何建立线索链表?如何建立线索链表?数据结构严蔚敏第6章一、一、何谓线索二叉树?何谓线索二叉树?遍历二叉树的结果是, 求得结点的一个线性序列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数据结构严蔚敏第6章指向该线性序列中的“前驱”和 “后继” 的指针指针,称作“线索线索”与其相应的二叉树,称作 “线索二叉树线索二叉树”包含 “线索” 的存储结构,称作 “线索链线索链表表”A B C D E F G H K^ D ^ C ^^ B E ^ 数据结构严蔚敏第6章对对线索链表线索链表中结点的约定:中结点的约定: 在二叉链表的结点中增加两个标志域增加两个标志域,并作如下规定:若该结点的左子树不空,若该结点的左子树不空,则Lchild域的指针指向其左子树, 且左标志域的值为“指针 Link”; 否则,Lchild域的指针指向其“前驱”, 且左标志的值为“线索 Thread” 。
数据结构严蔚敏第6章若该结点的右子树不空,若该结点的右子树不空,则rchild域的指针指向其右子树, 且右标志域的值为 “指针 Link”;否则,rchild域的指针指向其“后继”, 且右标志的值为“线索 Thread” 如此定义的二叉树的存储结构称作如此定义的二叉树的存储结构称作“线索链表线索链表”数据结构严蔚敏第6章typedef struct BiThrNod { TElemType data; struct BiThrNode *lchild, *rchild; // 左右指针 PointerThr LTag, RTag; // 左右标志} BiThrNode, *BiThrTree;线索链表的类型描述: typedef enum { Link, Thread } PointerThr; // Link==0:指针,Thread==1:线索数据结构严蔚敏第6章二、线索链表的遍历算法二、线索链表的遍历算法: for ( p = firstNode(T); p; p = Succ(p) ) Visit (p);由于索链表中添加了遍历中得到的“前驱”和“后继”的信息,从而简化了遍历的算法。
数据结构严蔚敏第6章例如例如: 对中序线索化链表的遍历算法对中序线索化链表的遍历算法 ※ ※ 中序遍历的第一个结点中序遍历的第一个结点 ?? ※ ※ 在中序线索化链表中结点的后继在中序线索化链表中结点的后继 ??左子树上处于“最左下最左下”(没有左子树)的结点若若无右子树,则为则为后继线索后继线索所指结点;否则为否则为对其右子树右子树进行中序遍历遍历时访问的第一个结点第一个结点数据结构严蔚敏第6章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进至其右子树根 }} // InOrderTraverse_Thr数据结构严蔚敏第6章 在中序遍历过程中修改结点的在中序遍历过程中修改结点的左、右指针域,以保存当前访问结左、右指针域,以保存当前访问结点的点的“前驱前驱”和和“后继后继”信息。
遍历过信息遍历过程中,附设指针程中,附设指针pre, 并始终保持指并始终保持指针针pre指向当前访问的、指针指向当前访问的、指针p所指所指结点的前驱结点的前驱三、如何建立线索链表?三、如何建立线索链表?数据结构严蔚敏第6章void InThreading(BiThrTree p) { if (p) { // 对以p为根的非空二叉树进行线索化 InThreading(p->lchild); // 左子树线索化 if (!p->lchild) // 建前驱线索 { p->LTag = Thread; p->lchild = pre; } if (!pre->rchild) // 建后继线索 { pre->RTag = Thread; pre->rchild = p; } pre = p; // 保持 pre 指向 p 的前驱 InThreading(p->rchild); // 右子树线索化 } // if} // InThreading数据结构严蔚敏第6章Status InOrderThreading(BiThrTree &Thrt, BiThrTree T) { // 构建中序线索链表 if (!(Thrt = (BiThrTree)malloc( sizeof( BiThrNode)))) exit (OVERFLOW); Thrt->LTag = Link; Thrt->RTag =Thread; Thrt->rchild = Thrt; // 添加头结点 return OK;} // InOrderThreading … … 数据结构严蔚敏第6章if (!T) Thrt->lchild = Thrt; else { Thrt->lchild = T; pre = Thrt; InThreading(T); pre->rchild = Thrt; // 处理最后一个结点 pre->RTag = Thread; Thrt->rchild = pre; }数据结构严蔚敏第6章 6.6 树和森林树和森林 的表示方法的表示方法数据结构严蔚敏第6章6.6.1 树的三种存储结构树的三种存储结构一、一、双亲表示法双亲表示法二、二、孩子链表表示法孩子链表表示法三、三、树的二叉链表树的二叉链表( (孩子孩子- -兄弟)兄弟) 存储表示法存储表示法数据结构严蔚敏第6章ABCDEFG0 A -11 B 02 C 03 D 04 E 2 5 F 26 G 5r=0n=6data parent一、双亲表示法一、双亲表示法:数据结构严蔚敏第6章 typedef struct PTNode { Elem data; int parent; // 双亲位置域 } PTNode; data parent#define MAX_TREE_SIZE 100结点结构结点结构:C语言的类型描述语言的类型描述: :数据结构严蔚敏第6章typedef struct { PTNode nodes [MAX_TREE_SIZE]; int r, n; // 根结点的位置和结点个数 } PTree;树结构树结构:数据结构严蔚敏第6章ABCDEFG0 A -11 B 02 C 03 D 04 E 25 F 26 G 4r=0n=6 data firstchild 1 2 34 56二、孩子链表表示法二、孩子链表表示法:数据结构严蔚敏第6章typedef struct CTNode { int child; struct CTNode *next; } *ChildPtr;孩子结点结构孩子结点结构: child nextC语言的类型描述语言的类型描述: :数据结构严蔚敏第6章 typedef struct { Elem data; ChildPtr firstchild; // 孩子链的头指针 } CTBox;双亲结点结构双亲结点结构 data firstchild数据结构严蔚敏第6章typedef struct { CTBox nodes[MAX_TREE_SIZE]; int n, r; // 结点数和根结点的位置 } CTree;树结构树结构:数据结构严蔚敏第6章ABCDEFG AB C E D F Groot AB C E D F G 三、树的二叉链表三、树的二叉链表 (孩子孩子-兄弟)存储表示兄弟)存储表示法法数据结构严蔚敏第6章typedef struct CSNode{ Elem data; struct CSNode *firstchild, *nextsibling;} CSNode, *CSTree;C语言的类型描述语言的类型描述: :结点结构结点结构: firstchild data nextsibling数据结构严蔚敏第6章 6.6.2 森林和二叉树的转换森林和二叉树的转换设设森林森林 F = ( T1, T2, …, Tn ); T1 = (root,,t11, t12, …, t1m);二叉树二叉树 B =( LBT, Node(root), RBT );数据结构严蔚敏第6章 由于二叉树可以用二叉链表表示,为了使一般树由于二叉树可以用二叉链表表示,为了使一般树也能用二叉链表表示,必须找出树与二叉树之间的关也能用二叉链表表示,必须找出树与二叉树之间的关系。
系 这样,给定一棵树,可以找到唯一的一棵二叉这样,给定一棵树,可以找到唯一的一棵二叉树与之对应树与之对应方法:方法:· 对每个孩子进行从左到右的排序;对每个孩子进行从左到右的排序; · 在兄弟之间加一条连线;在兄弟之间加一条连线; · 对每个结点,除了左孩子外,去除其与其余孩对每个结点,除了左孩子外,去除其与其余孩子之间的联系;子之间的联系; · 以根结点为轴心,将整个树顺时针转以根结点为轴心,将整个树顺时针转45度1、将树转换成二叉树、将树转换成二叉树的转换规则为:数据结构严蔚敏第6章 I A B C DE F G H(b) A B CD E G H FI(a)树转换为二叉树树转换为二叉树ABEFCDGHI(d)ABCDEFGHI(c)数据结构严蔚敏第6章2、由森林转换成二叉树、由森林转换成二叉树的转换规则为:若 F = Φ,则 B = Φ;否则, 由 ROOT( T1 ) 对应得到 Node(root); 由 (t11, t12, …, t1m ) 对应得到 LBT; 由 (T2, T3,…, Tn ) 对应得到 RBT。
数据结构严蔚敏第6章森林转换为二叉树森林转换为二叉树ADCBEFHIGJEFADCBHIGJADCBEFHIGJADCBEFHIGJ数据结构严蔚敏第6章3、由二叉树转换为森林、由二叉树转换为森林的转换规则为:若 B = Φ, 则 F = Φ;否则,由 Node(root) 对应得到 ROOT( T1 );由LBT 对应得到 ( t11, t12, …,,t1m);由RBT 对应得到 (T2, T3, …, Tn)数据结构严蔚敏第6章将二叉树转换成树或森林的方法如下:将二叉树转换成树或森林的方法如下:1.若某结点是其若某结点是其双亲的双亲的左孩子左孩子,则把该结点的右孩子、则把该结点的右孩子、右孩子的右孩子右孩子的右孩子…都与该结点的都与该结点的双亲双亲结点用线连起结点用线连起来来;2.删除原二叉树中所有的双亲结点与右孩子结点的连删除原二叉树中所有的双亲结点与右孩子结点的连线线.3.整理步骤整理步骤1、、2所得到的树或森林所得到的树或森林,使结构层次分明使结构层次分明.将二叉树转换为树或森林的另一种描述:将二叉树转换为树或森林的另一种描述:数据结构严蔚敏第6章ABEFCDGHI(a)ABEFCDGHI(b)将二叉树转换为树或森林将二叉树转换为树或森林ABEFCDGHI(c) 若若某某结结点点是是其其双双亲亲的的左左孩孩子子,则则把把该该结结点点的的右右孩孩子子、、右右孩孩子子的的右右孩孩子子…都都与与该该结结点点的的双双亲亲结结点用线连起来点用线连起来;删删除除原原二二叉叉树树中中所所有有的的双双亲亲结结点点与与右右孩子结点的连线孩子结点的连线.数据结构严蔚敏第6章 由此,树的各种操作均可对应二叉树的操作来完成。
应当注意的是,应当注意的是,和树对应的二叉树,其左、右子树的概念已改变为: 左是孩子,右是兄弟左是孩子,右是兄弟数据结构严蔚敏第6章6.7树和森林的遍历树和森林的遍历数据结构严蔚敏第6章一、树的遍历一、树的遍历二、森林的遍历二、森林的遍历三、树的遍历的应用三、树的遍历的应用数据结构严蔚敏第6章树的遍历可有三条搜索路径树的遍历可有三条搜索路径:按层次遍历按层次遍历:先根先根(次序次序)遍历遍历:后根后根(次序次序)遍历遍历: 若树不空,则先访问根结点,然后若树不空,则先访问根结点,然后依次先根遍历各棵子树依次先根遍历各棵子树 若树不空,则先依次后根遍历各棵若树不空,则先依次后根遍历各棵子树,然后访问根结点子树,然后访问根结点 若树不空,则自上而下自左至右若树不空,则自上而下自左至右访问树中每个结点访问树中每个结点数据结构严蔚敏第6章 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数据结构严蔚敏第6章 B C DE F G H I J K1.森林中第一棵树的根结点;2.森林中第一棵树的子树森林;3.森林中其它树构成的森林。
森林由三部分构成:数据结构严蔚敏第6章 若森林不空,则访问访问森林中第一棵树的根结点;先序遍历先序遍历森林中第一棵树的子树森林;先序遍历先序遍历森林中(除第一棵树之外)其 余树构成的森林1. 先序遍历先序遍历森林的遍历森林的遍历即:依次从左至右依次从左至右对森林中的每一棵树树进行先根遍历先根遍历数据结构严蔚敏第6章2.中序遍历2.中序遍历 若森林不空,则中序遍历中序遍历森林中第一棵树的子树森林;访问访问森林中第一棵树的根结点;中序遍历中序遍历森林中(除第一棵树之外)其 余树构成的森林即:依次从左至右依次从左至右对森林中的每一棵树树进行后根遍历后根遍历数据结构严蔚敏第6章 树的遍历和二叉树遍历树的遍历和二叉树遍历的对应关系的对应关系 ??先根遍历先根遍历后根遍历后根遍历树树二叉树二叉树森林森林先序遍历先序遍历先序遍历先序遍历中序遍历中序遍历中序遍历中序遍历数据结构严蔚敏第6章设树的存储结构为孩子兄弟链表设树的存储结构为孩子兄弟链表typedef struct CSNode{ Elem data; struct CSNode *firstchild, *nextsibling;} CSNode, *CSTree;一、求树的深度一、求树的深度二、输出树中所有从根到叶子的路径二、输出树中所有从根到叶子的路径三、建树的存储结构三、建树的存储结构数据结构严蔚敏第6章int TreeDepth(CSTree T) { if(!T) return 0; else { h1 = TreeDepth( T->firstchild ); h2 = TreeDepth( T->nextsibling); }} // TreeDepthreturn(max(h1+1, h2));一、求树的深度的算法:一、求树的深度的算法:数据结构严蔚敏第6章二、二、输出树中所有从根到叶子的路径的算法输出树中所有从根到叶子的路径的算法: 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 K数据结构严蔚敏第6章void 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// 输出二叉树上从根到所有叶子结点的路输出二叉树上从根到所有叶子结点的路径径数据结构严蔚敏第6章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// 输出森林中所有从根到叶的路径数据结构严蔚敏第6章三、建树的存储结构的算法三、建树的存储结构的算法: 和二叉树类似,不同的定义相应有不同的算法。
假设以二元组(F,,C)的形式自上而自上而下下、自左而右自左而右依次输入树的各边,建立树的孩子孩子-兄弟链表兄弟链表数据结构严蔚敏第6章ABCDEFG例如例如:对下列所示树的输入序列应为:(‘#’, ‘A’)(‘A’, ‘B’)(‘A’, ‘C’)(‘A’, ‘D’)(‘C’, ‘E’)(‘C’, ‘F’)(‘E’, ‘G’)ABCD(‘#’, ‘A’)(‘A’, ‘B’)(‘A’, ‘C’)(‘A’, ‘D’)(‘C’, ‘E’)可见,算法中需要一个队列队列保存已建好的结点的指针数据结构严蔚敏第6章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 … … 数据结构严蔚敏第6章GetHead(Q,s); // 取队列头元素(指针值)while (s->data != fa ) { // 查询双亲结点 DeQueue(Q,s); GetHead(Q,s);} if (!(s->firstchild)) { s->firstchild = p; r = p; } // 链接第一个孩子结点else { r->nextsibling = p; r = p; } // 链接其它孩子结点 数据结构严蔚敏第6章6.8 哈哈 夫夫 曼曼 树树 与与 哈哈 夫夫 曼曼 编编 码码§ 最优树的定义最优树的定义§ 如何构造最优树如何构造最优树§ 前缀编码前缀编码 数据结构严蔚敏第6章 一、最优树的定义一、最优树的定义树的路径长度树的路径长度定义为: 树中每个结点的路径长度之和。
结点的路径长度结点的路径长度定义为: 从根结点到该结点的路径上 分支的数目数据结构严蔚敏第6章1 12 2453 367PL=0+1+1+2+2+2+2=10树的路径长度用树的路径长度用PLPL表示1 12 245C67PL=0+1+1+2+2+3+3=12数据结构严蔚敏第6章 树的带权路径长度树的带权路径长度定义为: 树中所有叶子结点的带权路径长度结点的带权路径长度之和 WPL(T) = wklk (对所有叶子结点)其中其中:Wk为树中每个叶子结点的权; Lk为每个叶子结点到根的路径长度例如:例如:数据结构严蔚敏第6章27 9 75492WPL(T)= 72+52+23+43+92 =60WPL(T)= 74+94+53+42+21 =89 54数据结构严蔚敏第6章最优树最优树 在所有含 n 个叶子结点、并带相同权值的 m 叉树中,必存在一棵其带权路径长度取最小值带权路径长度取最小值的树,称为“最优树最优树”数据结构严蔚敏第6章 由原始数据生成森林由原始数据生成森林 根据给定的n个权值 {w1, w2, …, wn}, 构造 n 棵二叉树的集合 F = {T1, T2, … , Tn}, 其中每棵二叉树中均只含一个带权值 为 wi 的根结点,其左、右子树为空树;二、如何构造最优树二、如何构造最优树(1)(赫夫曼算法) 以二叉树为例:数据结构严蔚敏第6章 在 F 中选取其根结点的权值为最 小的两棵二叉树,分别作为左、 右子树构造一棵新的二叉树,并 置这棵新的二叉树根结点的权值 为其左、右子树根结点的权值之 和;(2)数据结构严蔚敏第6章 从F中删去这两棵树,同时加入 刚生成的新树; 重复 (2) 和 (3) 两步,直至 F 中只 含一棵树为止。
3)(4)数据结构严蔚敏第6章675cd(b)11b57cd(c)18a711cdb5624(d)abcd7524(a)例:给定权值例:给定权值{7{7,,5 5,,2 2,,4}4},,构造赫夫曼树构造赫夫曼树数据结构严蔚敏第6章9例例: : 已知权值已知权值 W={ 5, 6, 2, 9, 7 },构造赫夫曼树构造赫夫曼树562752769767139527数据结构严蔚敏第6章6713952795271667132900001111000110110111数据结构严蔚敏第6章三、哈夫曼树的应用三、哈夫曼树的应用1 1、判定树、判定树 在解决某些判定问题时,利用哈夫曼树可以得在解决某些判定问题时,利用哈夫曼树可以得到最佳判定算法到最佳判定算法例例1 将学生百分成绩按分数段分级的程序将学生百分成绩按分数段分级的程序若学生成绩分布是均匀的,可用图(若学生成绩分布是均匀的,可用图(a)二叉树结构二叉树结构来实现 a<60 a<70 a<80 a<90 不及格不及格中等中等良好良好优秀优秀及格及格YNYNYNYN(a)输入输入10000个个数据,则需进数据,则需进行行31500次比次比较。
较数据结构严蔚敏第6章分数分数0—5960—6970—7980—8990—99比例比例0.050.150.40.30.10 70≤a≤ 80 a<60 及格及格中等中等良好良好 80≤a<90 60≤a<70 不及格不及格优秀优秀YNYYYNNN(b) 不及格不及格Y a<90 a<80 a<70 a<60 优秀优秀中等中等及格及格良好良好YNNN(c)YYY 学生成绩分布不是均匀的情况:学生成绩分布不是均匀的情况:以以比比例例数数为为权权构构造造一一棵棵哈哈夫夫曼曼树树,,如(如(b)判断树所示判断树所示再再将将每每一一比比较较框框的的两两次次比比较较改改为为一一次,可得到(次,可得到(c)判定树输入输入10000个个数据,仅需进数据,仅需进行行22000次比次比较数据结构严蔚敏第6章2 2、前缀编码、前缀编码 “前缀编码”指的是,任何一个字符任何一个字符的编码都不是同一字符集中另一个字符的编码都不是同一字符集中另一个字符的编码的前缀的编码的前缀数据结构严蔚敏第6章3 3、赫夫曼编码、赫夫曼编码 利用赫夫曼树可以构造一种不等长的利用赫夫曼树可以构造一种不等长的二进制编码,并且构造所得的二进制编码,并且构造所得的赫夫曼编码赫夫曼编码是一种是一种最优前缀编码最优前缀编码,即使所传,即使所传电文的总电文的总长度最短长度最短。
数据结构严蔚敏第6章146833442200001111T;ACS各字符编码是各字符编码是 T ;; A C S 00 01 10 110 111上述电文编码:上述电文编码:1110011000方法:方法:((1))用用{ 2,,4,, 2,,3,, 3 }作作为为叶叶子子结结点点的的权权值值生生成成一一棵棵赫赫夫曼树,并将对应权值夫曼树,并将对应权值wi的叶子结点注明对应的字符;的叶子结点注明对应的字符;((2)约定左分支表示字符)约定左分支表示字符“0”,右分支表示字符,右分支表示字符‘1’((3))从从叶叶子子结结点点开开始始,,顺顺着着双双亲亲反反推推上上去去,,直直到到根根结结点点,,路路径径上上的的‘0’或或‘1’连连接接的的序序列列就就是是结结点点对对应应的的字字符符的的二二进进制编码的制编码的逆序逆序赫夫曼编码赫夫曼编码-----利用赫夫曼树构造通讯中电文编码(前缀码)利用赫夫曼树构造通讯中电文编码(前缀码)例:例:要传输的电文是要传输的电文是{CAS;;CAT;;SAT;;AT}要传输的字符集是要传输的字符集是 D={C,,A,,S,,T,, ;;}每个字符出现的频率是每个字符出现的频率是W={ 2,,4,, 2,,3,, 3 }注意:编码的总长度恰好为赫夫曼树的带权路径长。
注意:编码的总长度恰好为赫夫曼树的带权路径长数据结构严蔚敏第6章构造赫夫曼树的算法构造赫夫曼树的算法 为为了了实实现现构构造造赫赫夫夫曼曼树树的的算算法法,设设计计哈哈夫夫曼曼树树中中每每个结点类型如下:个结点类型如下: typedef struct {char data;/*结点值结点值*/float weight;/*权重权重*/int parent;/*双亲结点双亲结点*/int lchild;/*左孩子结点左孩子结点*/int rchild;/*右孩子结点右孩子结点*/ } HTNode,* HuffmanTree; 数据结构严蔚敏第6章。






![河南新冠肺炎文件-豫建科[2020]63号+豫建科〔2019〕282号](http://img.jinchutou.com/static_www/Images/s.gif)





