410 likes | 568 Views
第6章 树与二叉树. 第 6 章 树和二叉树. 6.1 树的定义及基本操作 6.2 二叉树 6.3 树与森林 6.4 哈夫曼树和判定树 6. 5 应用举例及分析 习题. 6.1 树的定义及基本操作 6.1.1 定义: 树( tree) 是 n 个结点的有限集合 T, 且满足以下条件: 当 n=0 时,集合为空,称空树。在任意非空树中: (1) 有且仅有一个特定的称为根( root) 的结点。( n=1)
E N D
第6章树和二叉树 6.1 树的定义及基本操作 6.2 二叉树 6.3 树与森林 6.4 哈夫曼树和判定树 6.5 应用举例及分析 习题
6.1 树的定义及基本操作 6.1.1 定义: 树(tree)是n个结点的有限集合T,且满足以下条件: 当n=0时,集合为空,称空树。在任意非空树中: (1)有且仅有一个特定的称为根(root)的结点。(n=1) (2)n>1,除根结点以外的其余结点可分为m(m>0)个互 为相交的有限集合T1,T2……Tm,其中的每个集合本身又是一棵树,并称其为根的子树(subtree) ★这是一个递归定义,反映了树的固有特性。即一棵树由根及若干棵子树构成,而子树又由更小的子树构成。
树的一般表示: ★树结构中结点之间有分支关系,层次关系,树中无环路
6.1.2 树的术语 树的结点:包括一个数据元素及若干指向其子树的分支。 结点的度:结点拥有的子树个数称为结点的度(degree) 树 的 度:树中所有结点的度的最大值为该树的度。 叶子(leaf):度为0的结点称叶子或终端结点。 分支结点:度不为0的结点称分支结点或非终端结点。 孩子,双亲和兄弟节点:节点的子树的根称为该节点的孩子结点(child)。而该节点称做孩子节点的双亲结点(parent)。具有同一双亲节点的孩子结点之间互称为兄弟(sibling)。
结点的层次(level):结点的层次从根开始算起,根的层次值为1,其余结点的层次值为双亲结点层次值加1。结点的层次(level):结点的层次从根开始算起,根的层次值为1,其余结点的层次值为双亲结点层次值加1。 树的深度(depth):树中结点的最大层次值称为树的深度或高度。 有序树与无序树:如果将树中结点的各个子树看成从左到右是有序的(即不能互换),则称该树为有序树,否则称为无序树。 森林(forest):森林是m(m≥0)棵互不相交的树的集合,删去一棵树的根,就得到一个森林。 树的逻辑结构特征可用树中结点的父子关系描述:树中任一结点可以有零个或多个孩子结点,但只能有一个双亲结点(根结点除外),树中只有根结点无前驱结点,所有叶子结点都无后继结点。显然,父子关系是非线性的,所以 树结构是非线性结构。
6.1.3 树的基本操作 1、TREEEMPTY(T) 判树空。若T为空树,返回TRUE, 否则返回FALSE。 2、ROOT (T) 求根。返回树T的根结点地址。 3、TREEDEPTH(T) 求树的深度。 4、VALUE(T,e) 返回结点的地址。 5、 Parent(T,e) 查找树中e结点的双亲结点地址。 6、Child(T,e,i) 返回e结点的第i个孩子结点的地址。 7、CREATE-TREE(T,T1,T2,…,Tk) 创建树。当k≥1,建立一棵以T为 根,以 T1,T2,…,Tk为子树的树。
6.2 二 叉 树 6.2.1 定义及操作: 一、相关定义 二叉树(binary tree):二叉树是n(n≥0)个结点的有限集合,n=0时为空二叉树;n≠0时,由一个根结点和两棵互不相交、分别称做左子树和右子树的子树构成。 满二叉树:一棵深度为k且有2k-1个结点的二叉树称为满二叉树。 完全二叉树:若一棵深度为k>1的二叉树中,第k-1层前构成一棵深度为k-1的满二叉树,第k层的结点不满2k-1个结点,但它们都满放在该层最左边,则此二叉树称为完全二叉树。
二、二叉树与树的区别与联系: 都有且仅有一个根,根无前驱,叶子无后继。二叉树中每个结点的度小于等于2,二叉树的子树有左右之分。 三、二叉树的五种基本形态 : 空树 只有根 右子树为空 左右子树非空 左子树为空
四、二叉树的基本操作 : 1、TREEEMPTY(BT) 判二叉树空。若BT为空树,返回TRUE, 否则返回FALSE。 2、ROOT (BT) 求根。返回树BT的根结点地址。 3、TREEDEPTH(BT) 求二叉树的深度。 4、VALUE(BT,e) 返回结点的地址。 5、 Parent(BT,e) 查找二叉树中e结点的双亲结点地址。 6、LChild(T,e,i) 返回e结点的左孩子结点的地址。 7、RChild(T,e,i) 返回e结点的右孩子结点的地址。 8、CREATE-TREE(BT,LBT,RBT) 创建二叉树。当k≥1,建立一棵以T为 根,以 T1,T2,…,Tk为子树的树。
6.2.2 二叉树的性质 性质1:二叉树第i层上的结点数目至多为ik-1(i≥1) 性质2:深度为k的二叉树至多有2k-1个结点(k1)结 点总数为公比为2的等比数列的前k项和 性质3:在任何一棵二叉树中,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1 性质4:具有n个结点的完全二叉树的深度为log2n +1
性质5:对一棵有n个结点的完全二叉树的结点按层自左向右编号,则对任一编号为i(1≤i≤)的结点有下列性质:性质5:对一棵有n个结点的完全二叉树的结点按层自左向右编号,则对任一编号为i(1≤i≤)的结点有下列性质: ⑴i=1时,则结点i是二叉树的根,若i>1,则结点的双亲结点是i/2 ; ⑵若2i≤n,则结点i有左孩子,左孩子的编号是2i。否则,结点无左孩子,并且是叶子结点; ⑶若2i+1≤n,则结点i有右孩子,右孩子的编号是2i+1,否则结点无右孩子 性质6:对一棵有n个结点的完全二叉树的结点按层自左向右编号,从编号为1的根结点开始到编号为n/2 的结点为止,都是有孩子结点的非叶子结点,余后的结点均是叶子结点。 n/2 编号的结点可能只有左孩子,也可能既有左孩子,又有右孩子。
6.2.3 二叉树的存储结构 1、顺序存储: 用一组地址连续的存储单元存放一棵二叉树的结点,一定要按结点的逻辑关系(父子关系)来存放。对顺序存储必须将二叉树转换为完全二叉树才能保证其正确的逻辑关系。 ★顺序存储的缺点是有时可能会造成空间浪费 二叉树的顺序存储结构: typedef struct {DATATYPE bt[MAXSIZE] int btnum ; }BTSEQ binary tree
A B C D E F A ^ B ^ ^ ^ C ^ ^ ^ ^ ^ ^ ^ D 完全二叉树的顺序存放 右单支二叉树的存放
2、链式存储: 二叉树的结点是由一个数据元素和分别指向左、右子树的指针构成。结点结构如下图。 链式存储二叉树结点的存储结构可定义为: typedef struct nodet {DATATYPE data ; struct nodet *lchild , * rchild ; }BTCHINALR 在一棵二叉树中,所有结点的类型都是BTCHINALR ,头指针root指向根结点,这就构成了二叉树的链式存储结构——二叉链表。
6.2.4 遍历二叉树 定义:遍历二叉树是指按某种规律对二叉树的每个结点依次访问的过程。对二叉树的遍历过程是将非线性结构的二叉树中的结点排列在一个线性序列上的过程。 二叉树的定义是递归的。一棵非空二叉树由树根、左子树和右子树组成,依次遍历这三部分,就可以遍历整个二叉树。 遍历二叉树的方式:用L、D、R表示左子树、根、右子树,共有种遍历树的方法,限定必须先左子树,后右子树,便有三种遍历树的方案: 1、先序遍历二叉树 2、中序遍历二叉树 3、后序遍历二叉树
1、先序遍历二叉树: (1) 访问根结点 (2) 先序遍历左子树 (3) 先序遍历右子树 递归算法: void preorder (BTCHINALR *bt) { if (bt!= NULL) {printf (“%c” , bt->data ) ; preorder (bt->lchild) ; preorder (bt->rchild) ; }}
2.中序遍历二叉树 (1) 中序遍历左子树 (2) 访问根结点 (3) 中序遍历右子树 递归算法: void inorder (BTCHINALR * bt) {if (bt!=NULL) {inorder (bt->lchild) print (“%c” , bt->data) ; inorder (bt->rchild) ; }}
3、后序遍历二叉树 (1)后序遍历左子树 (2)后序遍历右子树 (3)访问根结点 递归算法: void postorder (BTCHINALR * bt) {if (bt!= NULL) { postorder (bt->lchild) ; postorder(bt->rchild) ; printf(“%c” , bt->data) ; }}
6.3 树 与 森 林 6.3.1 树的常用存储结构 1、双亲表示法 data parent
特点:(1)求结点的双亲结点与根结点很方便特点:(1)求结点的双亲结点与根结点很方便 (2)求孩子需遍历整个结点 结构类型说明: typedef struct {DATATYPE data ; int parent ; } PTNODE ; typedef struct {PTNODE nodes [MAXSIZE ] ; int nodenum ; } PTTREE
2、孩子表示法 定义:把每个节点的孩子结点链接形成单链表。 由于树中每个节点的孩子结点的个数不确定,所以结点的描述要形成确定的格式是很难的。为此把结点的孩子结点以链表的形式表示而把结点信息及指向孩子链表的指针作为表结点组成一个向量表(顺序存储)。 特点:查找孩子结点很方便
3、孩子兄弟表示法: 又称二叉树表示法,与二叉树结构相同,所不同的是右孩子域用以表示结点的下一个兄弟(从左到右)。 二叉链表结构类型说明如下:typedef struct csnode {DATATYPE data ; struct csnode *firstchild , *nextsibling ; } CSNODE;
6.3.2 树、森林与二叉树的转换 一.树与二叉树的转换 1、树转换成二叉树:利用孩子兄弟表示法,可使一棵树变为二叉树。 方法: (1)加虚线:各兄弟间加虚线 (2)抹实线:每结点只留它与最左孩子的连线 (3)旋转改实:虚线改实线向下转45度 效果如下图
A A B B C D C D T 1 T1对应的二叉树
2、二叉树还原为一般树 方法: (1)加虚线:若某结点I是双亲的左孩子,则将该结点I的右孩子以及连续的右孩子都分别与结点I的双亲结点连线 (2)抹线:把有虚线的结点与原双亲的连线抹去 (3)整理:虚改实并按层排列
二、森林与二叉树的转换 1、森林转二叉树 方法: (1)将森林中的每棵树转换为二叉树,形成二叉树的森林 (2)按先后次序依次将后边一棵二叉树作为前边一棵二叉树根结点的右子树。第一棵树的根结点便是生成后的二叉树的根结点。
A A E G E B B C D F H I F G C D H I T 1 T 2 H T 3 森林F={T1,T2,T3} 森林对应的二叉树
2、二叉树还原为森林 方法: (1)抹线:将二叉树的根结点与其右孩子的连线以及连续地沿着右链不断地搜索到的所有右孩子的连线全部抹去,得到若干棵二叉树的森林。 (2)还原
6.4 哈夫曼树和判定树 哈夫曼树和判定树是树结构的两种应用方式。 6.4.1 哈夫曼树的定义及构造方法 (一)基本概念 1.路径长度:由树中一个结点到另一结点的分支构成这两个结点之间的路径,路径上分支的数目称为路径长度。 2.树的路径长度:从根结点到每一个结点的路径长度之和。
3、结点的带权路径长度:从根结点到某个结点的路径长度与该结点所带的权值的乘积。 4、树的带权路径长度:树中所有叶子结点的带权路径长度之和。记作:WPL=∑WiLi Wi、Li分别表示第i个叶子结点的权值和树根到该叶子结点之间的路径长度。 5、哈夫曼树:带权路径长度WPL最小的二叉树称哈夫曼树又称最优二叉树。
(二)哈夫曼算法(构造最优二叉树) 总从数值最小的结点开始层层向上构造二叉树,每个节点只用一次,同层左右结点不分次序。哈夫曼树中没有度为1的节点,n个叶子结点的哈夫曼树共有2n-1个结点。(上层n-1,本层n,共2n-1个结点)
6.4.2 哈夫曼编码 报文编码问题:想构造不等长最短编码,可用前缀编码,保证一个字符的编码不是另一编码的前缀。 解决方案:设每种字符在电文中出现频率为W,其编码长度为L,编码字符n个,则电文总长度为∑WL,正好是对应的哈夫曼树的WPL,可用哈夫曼树的原理构造二进制前缀编码,并使电文总长最短。 可用左树表示0,右树表示1,字符出现的频率为权值。 6.4.3 分类与判定 树可用于描述分类过程和处理判定的优化。
6.5 应用举例及分析 例6.1 写出二叉树的中根遍历非递归算法 解: Void inorder_notrecursive(BTCHINALR * bt) { BTCHINALR * q, * s[20] Int top = 0; Int bool = 1; q = bt; do{ While(q ! = NULL) {top ++ ; s[top] = q; q = q->lchild;} if(top= = 0) bool = 0; else { q = s[top]; top - -; printf(“%c\t”, q-> data); q = q->rchild;} }while(bool);}
E A,B,C,D 组成左子树 F,G,H,I,J,K 组成右子树 解析:先从根序列EBADCF HGI KJ可确定根结点是E。因中根序列是先遍历左子树,再遍历根结点,最后遍历右子树,所以从中根序列ABCDEFGHIJK可确定对应的二叉树的左子树由A,B,C,D结点组成,二叉树的右子树由F,G,H,I,J,K结点组成,二叉树的组成示意图如右图所示。 例6.2已知一棵二叉树的中根序列和先根序列分别为ABCDEFGHIJK和EBADCFHGIKJ,试画出这棵二叉树。
E B F,G,H,I,J,K组成 右子树 A组成 左子树 C,D组成 右子树 二叉树的左子树也是一棵二叉树,它的先根序列可从整个二叉树的先根序列EBADC FHGIKJ中找到,为BADC,由此可确定该左子树的根结点是B。从中根序列中的ABCK可确定B结点的左子树由A结点组成,B结点的右子树由C,D结点组成,二叉树的组成示意图可进一步展开,如右图所示。
E B F A D H C G I K J 该思路是个递归的思路,依此类推可得二叉树,如图所示。
例6.3 求二叉树中叶子结点的个数。 可以利用中序递归遍历算法求二叉树中叶子结点的个数,其算法如下: void inorder_leaf(BTCHINALR * bt) { if(bt ! = NULL) { inorder_leaf(bt->lchild); printf(bt->data); if(bt->lchild = = NULL) && (bt->rchild = = NULL) k++; inorder_leaf(bt->rchild);} } 上面函数中的k是全局变量,在主程序中先置零,在调用inorder_leaf后,k值就是二叉树BT中叶子结点的个数。
例6.4 求二叉树的深度。 可以用递归算法求二叉树的深度,其算法如下: int treehigh(BTCHINALR * bt) { int lh, rh, h; if(bt = = NULL) h = 0; else {lh = treehigh(bt->lchild); rh = treehigh(bt->rchild); h = (lh > rh ? lh : rh) + 1;} return h; }
习题 P97 6.3, 6.6 ,6.21