第6章 树和二叉树 (Tree & Binary Tree)

Slides:



Advertisements
Similar presentations
送健康.
Advertisements

第7章 樹與二元樹 (Trees and Binary Trees)
Chapter 06 Tree and binary tree 第六章 树和二叉树
教 师:曾晓东 电 话: E_mail: 计算机软件技术基础 教 师:曾晓东 电 话: E_mail:
第6章 二叉树和树 前面的章节主要讨论的是线性结构,二叉树和树属于非线性的结构。遍历非线性结构比线性结构要麻烦。
数据结构及应用算法教程(修订版) 配套课件.
組長:黃家逸 組員:殷浩賢、楊煜、吳家朗 毒品的害處.
计算机软件技术基础 数据结构与算法(4).
数据结构——树和二叉树 1/96.
引入树 线性表、堆栈和队列都是线性结构,它们的共同特点:一对一; 计算机对弈中的一个格局可能有多个后继格局,用线性结构难以描述。
第六章 树和二叉树.
第六章 树和二叉树.
第5章 树( Tree )和二叉树 5.1 树的基本概念 5.2 二叉树 5.3 遍历二叉树和线索二叉树 5.4 树和森林
CH6 树和二叉树 6.1 树的定义和基本术语 6.2 二叉树 6.3 遍历二叉树和线索二叉树 6.4 树和森林
第8章 查找 在英汉字典中查找某个英文单词的中文解释;在新华字典中查找某个汉字的读音、含义;在对数表、平方根表中查找某个数的对数、平方根;邮递员送信件要按收件人的地址确定位置等等。可以说查找是为了得到某个信息而常常进行的工作。 计算机、计算机网络使信息查询更快捷、方便、准确。要从计算机、计算机网络中查找特定的信息,就需要在计算机中存储包含该特定信息的表。如要从计算机中查找英文单词的中文解释,就需要存储类似英汉字典这样的信息表,以及对该表进行的查找操作。本章将讨论的问题即是“信息的存储和查找”。
第八章 查找.
数据结构 第六章 树与二叉树 深圳大学计算机系 蔡茂国.
第五章 树 东南大学计算机学院 方效林 本课件借鉴了清华大学殷人昆老师 和哈尔滨工业大学张岩老师的课件.
数据结构 第6章 树和二叉树 什么是树和二叉树?? 二叉树的遍历 数据结构.
§4 Additional Binary Tree Operations
第六章 树和二叉树 6.1 树的定义和基本术语 6.2 二叉树 6.3 遍历二叉树和线索二叉树 6.4 树和森林 6.6 赫夫曼树及其应用.
Tree(樹) 什麼是「樹」? 「樹」的範例 「樹」的定義 「樹」的表示法.
Chapter 5 Tree & Binary Tree
Chapter8 Binary and Other Trees
数 据 结 构 Ch.6 树 计 算 机 学 院 肖明军
第五章 数组和广义表.
第2章 线性表(三) 1/.
哈夫曼编码.
第12章 樹狀搜尋結構 (Search Trees)
第六章 二叉树和树 6.1树的基本概念 6.2二叉树 6.3二叉树遍历 6.4线索二叉树 6.5树和森林 6.6树的应用(霍夫曼树及其编码)
第六章 二叉树和树 6.1树的基本概念 6.2二叉树 6.3二叉树遍历 6.4线索二叉树 6.5树和森林 6.6树的应用(霍夫曼树及其编码)
程序设计期末复习 黎金宁
线性表小结 元素之间的线性关系 顺序表 顺序表:元素相邻存储 单链表:后继指针链接 一维数组 给定下标随机存取
第六章 树和二叉树.
第八章 查找表 8.1静态查找表 8.2动态查找表 8.3哈希表及其查找.
本章重点难点 重点:顺序查找、二分查找、二叉排序树查找以及散列表查找的基本思想和算法实现。
第 3 讲 线性表(一).
第三章 栈与队列 £3.1 栈 £3.3 队列 £3.2 栈的应用举例 £3.1.1 栈的定义 £3.1.2 栈的顺序存储结构
第3章 栈和队列(一).
第三章 栈和队列.
数据结构 Data Structure 中南大学 主讲人:王国军,郑瑾 中南大学信息院计科系
教 师:曾晓东 电 话: E_mail: 计算机软件技术基础 教 师:曾晓东 电 话: E_mail:
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
内容回顾 线性表的定义和特点 线性表的顺序存储及查找、插入和删除.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
第7章 树和二叉树 7.1 树 7.2 二叉树 7.3 以结点类为基础的二叉树设计 7.4 二叉树类 7.5 二叉树的分步遍历
数据结构 第9章 查找 什么是查找?? 静态查找 动态查找 哈希表 数据结构.
第九章 查找表 9.1静态查找表 9.2动态查找表 9.3哈希表及其查找.
6.6 Huffman树及其应用 王 玲.
6.3 遍历二叉树和线索二叉树(知识点二) 遍历二叉树 一、问题的提出
第六章 樹狀結構 T r e e 版權屬作者所有,非經作者 同意不得用於教學以外用途.
第九章 查找 2019/2/16.
第三章 栈和队列.
第四章串 4.1 串类型定义 4.2 串的表示和实现 4.3 串的模式匹配算法 4.4 串操作应用举例.
数据结构与数据库 习题课(2) 2016年11月25日.
Tree & Binary Tree.
王玲 第 2 章 线性表 王玲 2019/2/25.
第6章 树和二叉树 本章主题:树、二叉树 教学目的:掌握树和二叉树的类型定义、运算及存储结构 教学重点:树的各种表示、各种存储方式和运算,
第六章 树和二叉树 £6.1 树 £6.2 二叉树 £6. 3 二叉树的存储结构 £6.4 二叉树的遍历与线索化 £6.1.1 树的定义
第六章 树和二叉树 学习要点 理解树的定义和基本术语,重点了解二叉树的定义、性质、存储结构; 掌握二叉树遍历的递归算法及它的典型运算;
9.1 基本概念 9.2 线性表的查找 9.3 树表的查找 9.4 哈希表的查找 9.5 各种查找方法的比较
第7章 樹與二元樹(Trees and Binary Trees)
資料結構使用Java 第9章 樹(Tree).
Ch.9 查找 §9.1 基本概念 查找和排序是两个重要的运算
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
树和二叉树(一).
第二章 类型、对象、运算符和表达式.
单片机原理及应用 实践部分 主讲人:刘 强 四川工商学院单片机教学团队 单片机原理及应用 实践部分 主讲人:刘 强
#include <iostream.h>
本节内容 1.二叉排序树的定义 2.二叉排序树的查找操作 3.二叉排序树的插入操作 4.二叉排序树的删除操作 5.二叉排序树的总结
Presentation transcript:

第6章 树和二叉树 (Tree & Binary Tree) 6.1 树的基本概念 6.2 二叉树 6.3 遍历二叉树和线索二叉树 6.4 树和森林 6.5 赫夫曼树及其应用

6.5 Huffman树及其应用 一、Huffman树 二、Huffman编码 Huffman树 Huffman编码 最优二叉树 不等长编码 带权路径长度最短的树 Huffman树 最优二叉树 是通信中最经典的压缩编码 Huffman编码 不等长编码 2

WPL = wklk 经典之例: WPL= 36 WPL= 46 WPL= 35 树的带权路径长度如何计算? n 树的带权路径长度如何计算? 树中所有叶子结点的带权路径长度之和 经典之例: a b d c 7 5 2 4 (a) c d a b 2 4 5 7 (b) b d a c 7 5 2 4 (c) WPL= 36 WPL= 46 WPL= 35 Huffman树是WPL 最小的树 3

Huffman常译为赫夫曼、霍夫曼、哈夫曼、胡夫曼等 d e b a c f g 一、 Huffman树(最优二叉树) 若干术语: 路 径: 路径长度: 树的路径长度: 带权路径长度: 树的带权路径长度: Huffman树: 由一结点到另一结点间的分支所构成。 路径上的分支数目。 例如:a→e的路径长度= 2 10 从树根到每一结点的路径长度之和。 树长度= 结点到根的路径长度与结点上权的乘积(WPL) Weighted Path Length 即树中所有叶子结点的带权路径长度之和 带权路径长度最小的树。 Huffman常译为赫夫曼、霍夫曼、哈夫曼、胡夫曼等 4

WPL2=1bit×7+2bit×5+3bit×(2+4)=35 1. 构造Huffman树的基本思想: WPL最小的树 权值大的结点用短路径,权值小的结点用长路径。 讨论:Huffman树有什么用? 最小冗余编码、信息高效传输 例:设有4个字符d,i,a,n,出现的频度分别为7,5,2,4, 怎样编码才能使它们组成的报文在网络中传得最快? 法1:等长编码(如二进制编码) 令d=00,i=01,a=10,n=11,则: WPL1=2bit×(7+5+2+4)=36 法2:不等长编码(如Huffman编码) 令d=0;i=10,a=110,n=111,则: 频度高的信息用短码,低的用长码,传输效率肯定高! WPL2=1bit×7+2bit×5+3bit×(2+4)=35 明确:要实现Huffman编码,就要先构造Huffman树 5

2. 构造Huffman树的步骤(即Huffman算法): (1) 由给定的 n 个权值{ w1, w2, …, wn }构成n棵二叉树的集合F = { T1, T2, …, Tn } (即森林) ,其中每棵二叉树 Ti 中只有一个带权为 wi 的根结点,其左右子树均空。 (2) 在F 中选取两棵根结点权值最小的树 做为左右子树构造一棵新的二叉树,且让新二叉树根结点的权值等于其左右子树的根结点权值之和。 (3) 在F 中删去这两棵树,同时将新得到的二叉树加入 F中。 (4) 重复(2) 和(3) , 直到 F 只含一棵树为止。这棵树便是Huffman树。 总之,每次合并当前值最小的两个权。 (此树特征:没有度为1的结点) 怎样证明它就是WPL最小的最优二叉树?参考《信源编码》 6

具体操作步骤: step1:对权值进行合并、删除与替换 ——在权值集合{7,5,2,4}中,总是合并当前值最小的两个权 a. 初始 c. 合并{5} {6} d. 合并{7} {11} b. 合并{2} {4} 圆框表示内结点(合并后的权值) 谁左谁右?不规定就不会惟一 方框表示外结点(叶子,字符) 7

step2:按左“0”右“1” 对Huffman树的所有分支编号 ——将 Huffman树 与 Huffman编码 挂钩 1 d a i n Huffman编码结果:d=0, i=10, a=110, n=111 WPL=1bit×7+2bit×5+3bit(2+4)=35(小于等长码的WPL=36) 特征:每一码不会是另一码的前缀,译码时可惟一复原 Huffman编码也称为前缀码 8

二、Huffman编码 Huffman编码 不等长编码 如何将 Huffman树 与 Huffman编码 挂钩? 哈夫曼编码的基本思想是—— 出现概率大的信息用短码,概率小的用长码,最小冗余 是通信中最经典的压缩编码 Huffman编码 不等长编码 如何将 Huffman树 与 Huffman编码 挂钩? 按左“0”右“1” 对Huffman树的所有分支编号

本节重点:如何编程实现Huffman编码? 参见教材P147 建议1:Huffman树中结点的结构可设计成4或5分量形式: char weight parent lchild rchild 建议2: Huffman树的存储结构可采用顺序存储结构: 将整个Huffman树的结点存储在一个数组HT[1..n..m]中; (Huffman树内外结点总数m=2n-1) 各叶子结点的编码存储在另一“复合”数组HC[1..n]中。 (n个权值就是n个叶子,将对应n个不同码串) m=n0+n2=n+(n-1)=2n-1 即:先构造Huffman树HT 再求出n个权值/字符的Huffman编码HC

Huffman编码举例 例1【严题集6.26③】:假设用于通信的电文仅由8个字母 {a, b, c, d, e, f, g, h} 构成,它们在电文中出现的概率分别为{ 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10 },试为这8个字母设计哈夫曼编码。如果用0~7的二进制编码方案又如何? 【类同P148例2】 解:先将概率放大100倍,以方便构造哈夫曼树。 放大后的权值集合 w={ 7, 19, 2, 6, 32, 3, 21, 10 }, 按哈夫曼树构造规则(合并、删除、替换),可得到哈夫曼树。

w={ 7, 19, 2, 6, 32, 3, 21, 10 }在机内存储形式为: 双亲parent w p l r 100 左右孩子lchild,rchild 1 7 √ 11 21 19 40 60 32 2 19 √ 3 2 9 √ 28 10 4 6 √ e b g 5 32 √ 11 6 d 6 3 10 7 17 7 21 √ 2 3 5 8 10 √ a h 9 —- 5 3 6 10 11 — √ 12 11 4 9 c f 请注意:哈夫曼树样式不惟一,编程时应该有约定 — 17 1 8 √ 12 — 28 √ 10 11 13 — 40 2 7 60 100

对应的哈夫曼编码: 按左0右1标注 10 7 17 2 3 5 28 21 19 40 100 b c a 11 6 d 60 32 e g f h 1 符 编码 频率 a 0.07 b 0.19 c 0.02 d 0.06 e 0.32 f 0.03 g 0.21 h 0.10 符 编码 频率 a 0.07 b 0.19 c 0.02 d 0.06 e 0.32 f 0.03 g 0.21 h 0.10 1110 00 11010 1100 10 11011 01 1111 000 001 010 011 100 101 110 111 Huffman码的WPL=2(0.19+0.32+0.21) + 4(0.07+0.06+0.10) +5(0.02+0.03) =1.44+0.92+0.25=2.61 3(0.19+0.32+0.21+0.07+0.06+0.10+0.02+0.03)=3 二进制等长码的WPL=

另一种表示 :

自己上机练习说明:设字符集为26个英文字母,其出现频度如下表所示。 51 48 1 15 63 57 20 32 5 频度 z y x w v u t 字符 16 18 8 23 80 p 21 f q g r 47 h s o n m l k j 103 22 13 64 186 i e d c b a 空格 要求:先建Huffman树,再利用此树对任一字符串文件进行编码和译码——即设计一个Huffman编/译码器

Huffman树和Huffman树编码的存储表示: typedef struct{ unsigned int weight;//权值分量(可放大取整) unsigned int parent,lchild,rchild; //双亲和孩子分量 }HTNode,*HuffmanTree;//用动态数组存储Huffman树 typedef char**HuffmanCode; //动态数组存储Huffman编码表 *HuffmanTree或 HT向量样式: 指针型指针 双亲 r 9 2 19 7 l p w 3 1 HT[3].parent=9 ……

Huffman编码的实现: 先构造Huffman树HT, 再求出N个字符的Huffman编码HC。 *w存放n个字符的权值 int *w是表明w是一个指向int数组的指针,*w即取一个int,w++后*w取下一个元素……w这里和数组名(即指向该int数组的指针)等价。 void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n){ if (n<=1)return; m=2*n-1; //n 个叶子的HuffmanTree共有2n-1个结点; HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //0单元未用 for(p=HT+1,i=1; i<=n; ++i,++p,++w)*p={*w,0,0,0}; //给前n个单元初始化(原教材有误) for(;i<=m; ++i,++p)*p ={0,0,0,0}; //对叶子之后的存储单元清零 for(i=n+1;i<=m; ++i){ //建Huffman树(从n个叶子后开始存内结点) Select(HT, i-1, s1, s2); //在HT[1…i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2(教材未列此函数源码) HT[s1].parent=i; HT[s2].parent=i; //给双亲分量赋值 HT[i].lchild=s1; HT[i].rchild=s2; //给合并后的内结点赋孩子值 HT[i].weight=HT[s1].weight+ HT[s2].weight;} s2 r s1 9 i 2 7 l p w

(续前)再求出n个字符的Huffman编码HC 见P149图 (续前)再求出n个字符的Huffman编码HC HC=(HuffmanCode)malloc((n+1)*sizeof(char*)); //分配n个字符编码的头指针向量(一维数组) sizeof()可以对变量或变量类型运算,sizeof(char*)是一个char型指针的空间大小,如定义char *p,那么sizeof(p)就是结果,和sizeof(char*)等价。 cd=(char*) malloc(n*sizeof(char)); //分配求编码的临时最长空间 cd[n-1]=“\0”; //编码结束符(从cd[0]~cd[n-1]为合法空间) for(i=1;i<=n;++i) //逐个字符求Huffman编码 { start=n-1; //编码结束符位置 for(c=i,f=HT[i].parent; f!=0; c=f, f=HT[f].parent) //从叶子到根逆向求编码 if(HT[f].lchild==c) cd[--start]=“0”; else cd[--start]=“1”; HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间,并以数组形式存放各码串指针 strcpy(HC[i],&cd[start]); //从cd复制编码串到HC所指空间 } free(cd); //释放临时空间 }//HuffmanCoding

附:二叉树若干典型算法 (习题) 4例

A B C D E 1. 如何求二叉树的深度,或从某个结点开始的子树深度? 算法思路:只查各结点后继链表指针,若左(右)孩子的左(右)指针非空,则层次数加1;否则函数返回。 算法见附1 2. 如何按层次输出二叉树中所有结点? 算法思路:既然要求从上到下,从左到右,则利用队列存放各子树结点的指针是个好办法,此时绝不能用递归算法。 技巧:当根结点入队后,令其左、右孩子结点入队,而左孩子出队时又令它的左右孩子结点入队,……由此便可产生按层次输出的效果。 A B C D E 算法见附2

算法思路:完全二叉树的特点是:没有左子树空而右子树单独存在的情况(前k-1层都是满的,且第k层左边也满)。 3.如何判别给定二叉树是否为完全二叉树? 算法思路:完全二叉树的特点是:没有左子树空而右子树单独存在的情况(前k-1层都是满的,且第k层左边也满)。 技巧:按层次遍历方式,先把所有结点(不管当前结点是否有左右孩子)都入队列.若为完全二叉树,则层次遍历时得到的肯定是一个连续的不包含空指针的序列。如果序列中出现了空指针,则说明不是完全二叉树。 算法见附3 4 中序遍历的非递归算法如何实现? 算法思路:若不用递归,则要实现二叉树遍历的“嵌套”规则,必用堆栈(迭代方式) 。可直接用while语句和push/pop操作。参见教材P130-131程序。 算法见附4

附1:求二叉树的深度的算法 严题集6.44 ④ 法1:从根开始向下计算层次(比从叶子往上计算更简单) 附1:求二叉树的深度的算法 严题集6.44 ④ 法1:从根开始向下计算层次(比从叶子往上计算更简单) Void ABC(Bitree p, int l, int &h) { if p≠NIL then { l=l+1; if l>h then h=l; ABC(p->Lchild, l, h); ABC(p->Rchild, l, h); } l、h分别表示二叉树的层次数和深度。 想一想,l和h的初始值应设为多少? 开始调用时,应当用ABC(p, 0, 0) 若去掉h形参中的“&”号,则h不变化,算出的更深层数不能正确返回, h将永远为 0。

法2:递归时从叶子开始向上计数,层深者保留。 int BTreeDepth(Btree *BT) //*BT为二叉树某结点的指针 { int leftdep, rightdep; //设左右两个深度/层次计数器 if(BT==NULL) return(0); //当前结点指针为空则立即返回 else { leftdep= BTreeDepth(BT->left); //遍历当前结点左子树 rightdep=BTreeDepth(BT->right); //遍历当前结点右子树 if( leftdep>rightdep)return(leftdep+1); //从叶子起计数 else return(rightdep+1); } } //BTreeDepth

附2:层次遍历算法(需要利用队列) 严题集6.47 ④ 附2:层次遍历算法(需要利用队列) 严题集6.47 ④ void LayerOrder(Bitree T) //层序遍历二叉树 { InitQueue(Q); //建一个空队(初始化队列)   EnQueue(Q,T); //将一个结点插入队尾的函数 while( !QueueEmpty(Q) ) { DeQueue(Q, &p); //队首结点出队(送入p) visit(p); if(p->lchild) EnQueue(Q,p->lchild); //p的左孩子入队 if(p->rchild) EnQueue(Q,p->rchild); //p的右孩子入队 } }//LayerOrder 当孩子为空时不要将空指针入队

附3:判别给定二叉树是否为完全二叉树 严题集6.49 ④ int IsFull_Bitree(Bitree T)//是完全二叉树返回1,否则返0 { InitQueue(Q); //建队(初始化队列) flag=0; //标志初始化 EnQueue(Q,T); //结点T入队(空指针也要入队) while(!QueueEmpty(Q)) { DeQueue(Q,&p); //队首结点出队(送入p) if(!p) flag=1; //队首结点为空则标志变,但也许是队尾? else if(flag)return 0; //下一轮才能确定是不是完全二叉树 else { EnQueue(Q,p->lchild); //不管孩子是否为空,都入队列 EnQueue(Q,p->rchild); } }//while return 1; //执行至此必为队空且中间无空指针,完全二叉 }//IsFull_Bitree

附4:中序遍历的非递归算法(或称迭代算法)见教材P131 Status InOrderTrav( BiTree T, Status(*Visit)(TElemType e) ) { //此处Visit意思是对元素e的一种操作 InitStack(S);p=T; //初始化栈 while( p || !StackEmpty(S) ) //树不空或栈不空则开始遍历 { if( p ){Push(S,p);p=p->lchild;}//根指针进栈,遍历左子树 else{ Pop(S,p); //根指针退栈 if(! Visit(p->data))return ERROR; //访问根结点 p=p->rchild; //遍历右子树 } //else } //while return OK; } //InOrderTrav

本 章 小 结 第6章结束 存储结构 遍历 1、定义和性质 1:2, 性质有3+2条 树 森林 2、存储结构 3、遍历 遍历 先序遍历 后序遍历 孩子—兄弟 本 章 小 结 存储结构 遍历 1、定义和性质 1:2, 性质有3+2条 树 二叉树 森林 顺序结构 链式结构 2、存储结构 二叉链表 三叉链表 中序遍历 后序遍历 先序遍历 层次遍历 3、遍历 遍历 Huffman树 应用 先序线索树 中序线索树 后序线索树 先 序 遍 历 中 4、线索化 线索树 Huffman编码 第6章结束