赫夫曼树及其编码
最优二叉树(哈夫曼树)的构建及编码
最优⼆叉树(哈夫曼树)的构建及编码参考:数据结构教程(第五版)李春葆主编⼀,概述1,概念 结点的带权路径长度: 从根节点到该结点之间的路径长度与该结点上权的乘积。
树的带权路径长度: 树中所有叶结点的带权路径长度之和。
2,哈夫曼树(Huffman Tree) 给定 n 个权值作为 n 个叶⼦结点,构造⼀棵⼆叉树,若该树的带权路径长度达到最⼩,则称这样的⼆叉树为最优⼆叉树,也称为哈夫曼树。
哈夫曼树是带权路径长度最短的树,权值较⼤的结点离根较近。
⼆,哈夫曼树的构建1,思考 要实现哈夫曼树⾸先有个问题摆在眼前,那就是哈夫曼树⽤什么数据结构表⽰? ⾸先,我们想到的肯定数组了,因为数组是最简单和⽅便的。
⽤数组表⽰⼆叉树有两种⽅法: 第⼀种适⽤于所有的树。
即利⽤树的每个结点最多只有⼀个⽗节点这种特性,⽤ p[ i ] 表⽰ i 结点的根节点,进⽽表⽰树的⽅法。
但这种⽅法是有缺陷的,权重的值需要另设⼀个数组表⽰;每次找⼦节点都要遍历⼀遍数组,⼗分浪费时间。
第⼆种只适⽤于⼆叉树。
即利⽤⼆叉树每个结点最多只有两个⼦节点的特点。
从下标 0 开始表⽰根节点,编号为 i 结点即为 2 * i + 1 和 2 * i + 2,⽗节点为 ( i - 1) / 2,没有⽤到的空间⽤ -1 表⽰。
但这种⽅法也有问题,即哈夫曼树是从叶结点⾃下往上构建的,⼀开始树叶的位置会因为⽆法确定⾃⾝的深度⽽⽆法确定,从⽽⽆法构造。
既然如此,只能⽤⽐较⿇烦的结构体数组表⽰⼆叉树了。
typedef struct HTNode // 哈夫曼树结点{double w; // 权重int p, lc, rc;}htn;2,算法思想 感觉⽐较偏向于贪⼼,权重最⼩的叶⼦节点要离根节点越远,⼜因为我们是从叶⼦结点开始构造最优树的,所以肯定是从最远的结点开始构造,即权重最⼩的结点开始构造。
所以先选择权重最⼩的两个结点,构造⼀棵⼩⼆叉树。
然后那两个最⼩权值的结点因为已经构造完了,不会在⽤了,就不去考虑它了,将新⽣成的根节点作为新的叶⼦节加⼊剩下的叶⼦节点,⼜因为该根节点要能代表整个以它为根节点的⼆叉树的权重,所以其权值要为其所有⼦节点的权重之和。
5.2哈夫曼树与哈夫曼编码
T->Left = DeleteMin(H);
/*从最小堆中删除一个结点,作为新T的左子结点*/
T->Right = DeleteMin(H);
/*从最小堆中删除一个结点,作为新T的右子结点*/
T->Weight = T->Left->Weight+T->Right->Weight;
/*计算新权值*/
可以无二义地解码
二叉树用于编码
用二叉树进行编码: (1)左右分支:0、1 (2)字符只在叶结点上
四个字符的频率: a:4, u:1, x:2, z:1
1
0
1
0 01
axuz
01
01 01 auxz
Cost ( aaaxuaxz 00010110010111) = 14 + 31 + 22 + 31 = 14
分数段 0-59 60-69 70-79 80-89 90-100 比例 0.05 0.15 0.40 0.30 0.10
修改判定树:
yes score<80 no
yes score<70 no
yes score<90 no
yes score<60 no
grade=3
grade=4 grade=5
【分析】 (1)用等长ASCII编码:58 ×8 = 464位; (2)用等长3位编码:58 ×3 = 174位; (3)不等长编码:出现频率高的字符用的编码短些,出现频率低 的字符则可以编码长些?
怎么进行不等长编码? 如何避免二义性?
前缀码prefix code:任何字符的编码都不是另一字符编码的前缀
判定树:
c语言哈夫曼树的构造及编码
c语言哈夫曼树的构造及编码一、哈夫曼树概述哈夫曼树是一种特殊的二叉树,它的构建基于贪心算法。
它的主要应用是在数据压缩和编码中,可以将频率高的字符用较短的编码表示,从而减小数据存储和传输时所需的空间和时间。
二、哈夫曼树的构造1. 哈夫曼树的定义哈夫曼树是一棵带权路径长度最短的二叉树。
带权路径长度是指所有叶子节点到根节点之间路径长度与其权值乘积之和。
2. 构造步骤(1) 将待编码字符按照出现频率从小到大排序。
(2) 取出两个权值最小的节点作为左右子节点,构建一棵新的二叉树。
(3) 将新构建的二叉树加入到原来排序后队列中。
(4) 重复上述步骤,直到队列只剩下一个节点,该节点即为哈夫曼树的根节点。
3. C语言代码实现以下代码实现了一个简单版哈夫曼树构造函数:```ctypedef struct TreeNode {int weight; // 权重值struct TreeNode *leftChild; // 左子节点指针struct TreeNode *rightChild; // 右子节点指针} TreeNode;// 构造哈夫曼树函数TreeNode* createHuffmanTree(int* weights, int n) {// 根据权值数组构建节点队列,每个节点都是一棵单独的二叉树TreeNode** nodes = (TreeNode**)malloc(sizeof(TreeNode*) * n);for (int i = 0; i < n; i++) {nodes[i] = (TreeNode*)malloc(sizeof(TreeNode));nodes[i]->weight = weights[i];nodes[i]->leftChild = NULL;nodes[i]->rightChild = NULL;}// 构建哈夫曼树while (n > 1) {int minIndex1 = -1, minIndex2 = -1;for (int i = 0; i < n; i++) {if (nodes[i] != NULL) {if (minIndex1 == -1 || nodes[i]->weight < nodes[minIndex1]->weight) {minIndex2 = minIndex1;minIndex1 = i;} else if (minIndex2 == -1 || nodes[i]->weight < nodes[minIndex2]->weight) {minIndex2 = i;}}}TreeNode* newNode =(TreeNode*)malloc(sizeof(TreeNode));newNode->weight = nodes[minIndex1]->weight + nodes[minIndex2]->weight;newNode->leftChild = nodes[minIndex1];newNode->rightChild = nodes[minIndex2];// 将新构建的二叉树加入到原来排序后队列中nodes[minIndex1] = newNode;nodes[minIndex2] = NULL;n--;}return nodes[minIndex1];}```三、哈夫曼编码1. 哈夫曼编码的定义哈夫曼编码是一种前缀编码方式,它将每个字符的编码表示为二进制串。
哈夫曼树及哈夫曼编码的算法实现c语言
哈夫曼树及哈夫曼编码的算法实现c语言1.引言1.1 概述哈夫曼树及哈夫曼编码是数据压缩和编码中常用的重要算法。
哈夫曼树由大卫·哈夫曼于1952年提出,用于根据字符出现的频率构建一种最优的前缀编码方式。
而哈夫曼编码则是根据哈夫曼树构建的编码表将字符进行编码的过程。
在现代通信和计算机领域,数据传输和存储中往往需要大量的空间。
为了有效利用有限的资源,减少数据的存储和传输成本,数据压缩成为一个重要的技术。
而哈夫曼树及哈夫曼编码正是数据压缩中常用的技术之一。
哈夫曼树的概念及原理是基于字符的频率和概率进行构建的。
在哈夫曼树中,字符出现频率越高的节点越接近根节点,出现频率越低的节点离根节点越远。
这种构建方式保证了哈夫曼树的最优性,即最小化编码的总长度。
哈夫曼编码的算法实现是根据哈夫曼树构建的编码表进行的。
编码表中,每个字符都与一段二进制编码相对应。
在进行数据压缩和解压缩时,通过查表的方式将字符转化为相应的二进制编码,或将二进制编码解析为原始字符。
本文旨在介绍哈夫曼树及哈夫曼编码的概念和原理,并通过C语言实现算法。
通过深入理解哈夫曼树及哈夫曼编码的实现过程,可以更好地理解数据压缩和编码的原理,为后续的研究和应用提供基础。
接下来,我们将首先介绍哈夫曼树的概念和原理,然后详细讲解哈夫曼编码的算法实现。
最后,我们将总结哈夫曼树及哈夫曼编码的重要性,并提出对哈夫曼树和哈夫曼编码进一步研究的方向。
让我们一起深入探索哈夫曼树及哈夫曼编码的奥秘吧!1.2 文章结构文章结构部分的内容可以包括以下内容:文章结构部分主要介绍了本文的组织结构和各个章节的内容概述,以帮助读者更好地理解全文的逻辑结构和内容安排。
首先,本文包括引言、正文和结论三个部分。
引言部分主要对哈夫曼树及哈夫曼编码的算法实现进行了概述,包括相关的概念、原理和目的。
正文部分则深入介绍了哈夫曼树的概念和原理,以及哈夫曼编码的算法实现。
最后,结论部分对本文的主要内容进行了总结,并提出了对哈夫曼树和哈夫曼编码的进一步研究方向。
哈夫曼(huffman)树和哈夫曼编码
哈夫曼(huffman)树和哈夫曼编码讨论QQ群:待定哈夫曼树哈夫曼树也叫最优二叉树(哈夫曼树)问题:什么是哈夫曼树?例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80~89分: B,70~79分: C,60~69分: D,<60分: E。
if (a < 60){b = 'E';}else if (a < 70) {b = ‘D’;}else if (a<80) {b = ‘C’;}else if (a<90){b = ‘B’;}else {b = ‘A’;}判别树:用于描述分类过程的二叉树。
如果每次输入量都很大,那么应该考虑程序运行的时间如果学生的总成绩数据有10000条,则5%的数据需1 次比较,15%的数据需 2 次比较,40%的数据需 3 次比较,40%的数据需 4 次比较,因此 10000 个数据比较的次数为: 10000 (5%+2×15%+3×40%+4×40%)=31500次此种形状的二叉树,需要的比较次数是:10000 (3×20%+2×80%)=22000次,显然:两种判别树的效率是不一样的。
问题:能不能找到一种效率最高的判别树呢?那就是哈夫曼树回忆树的基本概念和术语路径:若树中存在一个结点序列k1,k2,…,kj,使得ki是ki+1的双亲,则称该结点序列是从k1到kj的一条路径。
路径长度:等于路径上的结点数减1。
结点的权:在许多应用中,常常将树中的结点赋予一个有意义的数,称为该结点的权。
结点的带权路径长度:是指该结点到树根之间的路径长度与该结点上权的乘积。
树的带权路径长度:树中所有叶子结点的带权路径长度之和,通常记作:其中,n表示叶子结点的数目,wi和li分别表示叶子结点ki的权值和树根结点到叶子结点ki之间的路径长度。
赫夫曼树(哈夫曼树,huffman树)定义:在权为w1,w2,…,wn的n个叶子结点的所有二叉树中,带权路径长度WPL最小的二叉树称为赫夫曼树或最优二叉树。
数据结构(二十七)Huffman树和Huffman编码
数据结构(⼆⼗七)Huffman树和Huffman编码 Huffman树是⼀种在编码技术⽅⾯得到⼴泛应⽤的⼆叉树,它也是⼀种最优⼆叉树。
⼀、霍夫曼树的基本概念 1.结点的路径和结点的路径长度:结点间的路径是指从⼀个结点到另⼀个结点所经历的结点和分⽀序列。
结点的路径长度是指从根结点到该结点间的路径上的分⽀数⽬。
2.结点的权和结点的带权路径长度:结点的权是指结点被赋予⼀个具有某种实际意义的数值。
结点的带权路径长度是该结点的路径长度与结点的权值的乘积。
3.树的长度和树的带权路径长度:树的长度就是从根结点到每⼀结点的路径长度之和。
树的带权路径长度就是所有叶结点的带权路径长度之和。
4.最优⼆叉树:带权路径长度WPL最⼩的⼆叉树称为霍夫曼树(Huffman Tree)或最优⼆叉树。
⼆叉树a的WPL = 5 x 1 + 15 x 2 + 40 x 3 + 30 x 4 + 10 x 5 = 315 ⼆叉树b的WPL = 5 x 3 + 15 x 3 + 40 x 2 + 30 x 2 + 10 x 2 = 220 ⼆、霍夫曼树的构造⽅法由给定的n个权值{w1,w2,... ,wn}构成由n棵⼆叉树所构成的森林F={T1,T2,...,Tn},其中每棵⼆叉树只有⼀个根结点,并且根结点的权值对应于w1,w2,...,wn在F中选取根结点的权值最⼩的两棵树,分别把它们作为左⼦树和右⼦树去构造⼀棵新的⼆叉树,新的⼆叉树的各结点的权值为左、右⼦树的权值之和在F中删除上⼀步中选中的两棵树,并把新产⽣的⼆叉树加⼊到F中重复第2步和第3步,直到F只含⼀棵树为⽌,这棵树就是霍夫曼树。
三、霍夫曼编码 霍夫曼树更⼤的⽬的是解决了当年远距离通信(电报)的数据传输的最优化问题。
霍夫曼编码的定义:设需要编码的字符集为(d1,d2,...,dn)各个字符在电⽂中出现的次数或者频率集合为{w1,w2,...,wn},以d1,d2,...,dn为叶⼦结点,w1,w2,...,wn作为相应叶⼦结点的权值来构造⼀棵霍夫曼树。
数据结构c+python代码6:哈夫曼树构造及编码
数据结构c+python代码6:哈夫曼树构造及编码⾸先介绍⼀下什么是哈夫曼树?给定N个权值作为N个叶⼦结点,构造⼀棵⼆叉树,若该树的带权路径长度达到最⼩,称这样的⼆叉树为最优⼆叉树,也称为哈夫曼树(Huffman Tree)。
哈夫曼树是带权路径长度最短的树,权值较⼤的结点离根较近。
哈夫曼树⼜称为最优树.1、路径和路径长度在⼀棵树中,从⼀个结点往下可以达到的孩⼦或孙⼦结点之间的通路,称为路径。
通路中分⽀的数⽬称为路径长度。
若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度哈夫曼树哈夫曼树(3张)若将树中结点赋给⼀个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度树的带权路径长度规定为所有叶⼦结点的带权路径长度之和,记为WPL。
哈夫曼树的构造假设有n个权值,则构造出的哈夫曼树有n个叶⼦结点。
n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有⼀个结点);(2) 在森林中选出两个根结点的权值最⼩的树合并,作为⼀棵新树的左、右⼦树,且新树的根结点权值为其左、右⼦树根结点权值之和;(3)从森林中删除选取的两棵树,并将新树加⼊森林;(4)重复(2)、(3)步,直到森林中只剩⼀棵树为⽌,该树即为所求得的哈夫曼树。
c代码过程分析构造哈夫曼树算法的实现可以分成两⼤部分:1、初始化:⾸先动态申请2n个单元;然后循环2n-1次,从1号单元开始,依次将1⾄2n-1所有单元中的双亲、左孩⼦、右孩⼦的下标都初始化为0;最后再循环n次,输⼊前n个单元中叶⼦节点的权值。
2、创建树:循环n-1次,通过n-1次的选择、删除与合并来创建哈夫曼树。
选择是从当前森林中选择双亲为0且权值最⼩的两个树跟节点是s1和s2;删除是指将节点s1和s2的双亲改为⾮0;合并就是将s1和s2的权值和作为⼀个新节点的权值依次存⼊到数组的第n+1之后的单元中,同时记录这个新节点左孩⼦的下标为s1,右孩⼦的下标为s2。
用Huffman树进行编码与解码算法
一、目的和要求1、通过本实验,熟悉二叉树、Huffman树的基本概念,掌握二叉树的存储结构及各种算法;2、熟悉用Huffman树进行电文的加密与解密算法。
二、实验内容1、Huffman树的存储方式;2、加密与解密算法在电文编码中的应用。
三、实验原理给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)。
Huffman树是一种特殊的二叉树,其叶结点的编码是一种前缀码,同时,通过统计字符的频度,能够达到编码电文的最小化。
哈夫曼树的构造假设有n个权值,则构造出的哈夫曼树有n个叶子结点。
n个权值分别设为w1、w2、…、wn,则哈夫曼树的构造规则为:(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;(3)从森林中删除选取的两棵树,并将新树加入森林;(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
四、实验步骤1、统计电文中字符的出现频率;2. 用统计频率建立Hffman树;3.生成前缀码;4.建立huffman树的解码算法;5.用随机输入的电文完成编码与解码过程。
五、程序源代码及注释#include<stdio.h>#include<malloc.h>#include<string.h>#include<stdio.h>#include<string.h>#include<malloc.h>typedef struct{int weight;unsigned int parent,lchild,rchild;char c;}Haffman;typedef struct {char a;int n;}cc;void input(char c[100],cc c1[100],int *n){int i,j,t,k,flag=0;i=strlen(c);c1[0].n=1;*n=1;c1[0].a=c[0];for(j=1;j<i;j++){for(t=0;t<*n;t++)if(c1[t].a==c[j]){flag=1;c1[t].n=c1[t].n+1;}if(flag!=1){c1[*n].n=1;c1[*n].a=c[j];*n=*n+1;}flag=0;}}void select(Haffman *ha,int *s1,int *s2,int n){int i,w=100;for(i=1;i<2*n;i++){if((ha+i)->parent==0&&(ha+i)->weight!=0)if((ha+i)->weight<w){*s1=i;w=(ha+i)->weight;}}(ha+(*s1))->parent=1;w=100;for(i=1;i<2*n;i++){if((ha+i)->parent==0&&(ha+i)->weight!=0)if((ha+i)->weight<w){*s2=i;w=(ha+i)->weight;}}(ha+(*s2))->parent=1;}void haffuman(char **c2, Haffman * haff,cc c1[100],int n) {int i,s1,s2,j,t;char *c;for(i=1;i<n+1;i++){(haff+i)->lchild=0;(haff+i)->parent=0;(haff+i)->rchild=0;(haff+i)->weight=c1[i-1].n;(haff+i)->c=c1[i-1].a;}for(i=1+n;i<2*n;i++){(haff+i)->lchild=0;(haff+i)->parent=0;(haff+i)->rchild=0;(haff+i)->weight=0;(haff+i)->c='\0';}for(i=n+1;i<2*n;i++){select(haff,&s1,&s2,n);(haff+i)->weight=(haff+s1)->weight+(haff+s2)->weight;(haff+i)->lchild=s1;(haff+i)->rchild=s2;(haff+s1)->parent=i;(haff+s2)->parent=i;}c=(char*)malloc(10*sizeof(char));for(i=1;i<=n;i++){t=i;j=9;*(c+j)='\0';while((haff+t)->parent!=0){j=j-1;if((haff+(haff+t)->parent)->lchild==t)*(c+j)='0';else*(c+j)='1';t=(haff+t)->parent;}*(c2+i)=(char*)malloc((10-j)*sizeof(char));strcpy(*(c2+i),(c+j));}}void main(){Haffman *haff;char **c2;cc c1[100];int i, n=0;char c[100];printf("请输入电文\n");scanf("%s",&c);input(c,c1,&n);haff=(Haffman *)malloc(2*n*sizeof(Haffman ));c2=(char**)malloc((n+1)*sizeof(char *));haffuman(c2, haff, c1,n);printf("字符权编码 \n");for(i=1;i<=n;i++){printf("%c ",(haff+i)->c);printf("%d ",(haff+i)->weight);printf("%s\n", *(c2+i));printf("\n");}}程序运行截图:。
赫夫曼
结论: 结论:
设计电文总长最短的二进制前缀编码的问 题即为: 题即为: 以n种字符出现的频率作为权,设计一棵赫 种字符出现的频率作为权, 夫曼树的问题, 夫曼树的问题,由此得到的二进制前缀编码称 为赫夫曼编码。 赫夫曼编码。
30
赫夫曼树及其应用 小结:
赫夫曼树算法思路: 1、赫夫曼树算法思路: ——权值大的结点用短路径,权值小的结点 权值大的结点用短路径, 权值大的结点用短路径 用长路径。 用长路径。 2、构造赫夫曼树的步骤: 构造赫夫曼树的步骤: ——对权值的合并、删除与替换。 对权值的合并、 对权值的合并 删除与替换。 赫夫曼编码规则:( :(左 3、赫夫曼编码规则:(左“0” 右“1”) ) ——又称为前缀码、最小冗余编码、紧致码 又称为前缀码、 又称为前缀码 最小冗余编码、 等等,它是数据压缩学的基础。 等等,它是数据压缩学的基础。
3
赫夫曼树(Huffman树 赫夫曼树(Huffman树)
定义: 定义:
带权路径长度(WPL) 带权路径长度(WPL)达到最小的扩充二叉 树即为赫夫曼树(最优二叉树) 树即为赫夫曼树(最优二叉树)。
7 5 2 4
4
赫夫曼树(Huffman树 赫夫曼树(Huffman树)
具有不同带权路径长度的扩充二叉树: 具有不同带权路径长度的扩充二叉树:
23
赫夫曼编码
假设一段报文如下: 假设一段报文如下: CAST CAST SAT AT A TASA 字符集合是 { C, A, S, T },各个字符 }, 出现的频度(次数) }。 出现的频度(次数)是 W={ 2, 7, 4, 5 }。 如何实现编码? 如何实现编码?
24
赫夫曼编码
1、若给每个字符以等长编码 C : 01 A : 00 S : 11 T : 10 W={
数据结构课程设计 哈夫曼树及编码
HUFFMAN树及编码1.需求分析随机输入一篇英文文章(或读一个TXT文件),生成并显示HUFFMAN树,输出每个字母的HUFFMAN编码,判断ASCII编码与HUFFMAN编码对本篇报文长度节省效果。
(a) 输入的形式为键盘随机输入,输入的字符串长度在10000以内;(b) 输出HUFFMAN树的存储结构;(c) 程序功能为输入英文文章中每个字母的HUFFMAN编码,以及与ASCLL 码编码长度的比较结果;(d) 测试数据:正确的输入一篇英文文章,会输出各个字母的HUFFMAN编码。
错误的输入即其输出输入错误。
2. 概要设计首先统计英文文章中各个字母的个数作为权值,再统计出现的字母的个数,以决定所构造赫夫曼树的节点个数,之后便开始构造赫夫曼树,再构造每个节点的哈夫曼编码。
所设计的抽象数据类型如下:typedef struct{unsigned int weight; //权值unsigned int parent, lchild, rchild; //双亲,左孩子,右孩子} HTNode, * HuffmanTree;typedef char * * HuffmanCode;所设计的函数如下:int min(HuffmanTree HT, int i) 找出所有权值中最小的那个。
void Select(HuffmanTree HT, int i, int &s1, int &s2) 找出每次最小的两个权值最小的权值。
Status HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n) 构造赫夫曼树并构造每个字母的赫夫曼编码。
void PrintHT(HuffmanTree HT, int n)输出赫夫曼树的存储结构。
3. 详细设计int min(HuffmanTree HT, int i){int j, flag = 1;unsigned int k = 10000;for(j = 1; j <= i; j++){if(HT[j].weight < k && HT[j].parent == 0){k = HT[j].weight, flag = j;}//if}HT[flag].parent = 1;return flag;}void Select(HuffmanTree HT, int i, int &s1, int &s2){int j;s1 = min(HT, i);s2 = min(HT, i);if(s1 > s2){j = s1;s1 = s2;s2 = j;}}Status HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n) {int s1, s2, i, start, f;char *cd;HuffmanTree p = NULL;//p是工作指针,指向赫夫曼树中的结点if(n <= 1){return ERROR;}int m = 2 * n - 1; //n个字符构造的赫夫曼树共有m = 2*n-1个结点printf("->待构造的赫夫曼树共有2 ×%d - 1 = %d个结点\n", n, m);if(!(HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode))))//申请赫夫曼树结点占用的内存空间,0号单元不用{exit(OVERFLOW);}//ifprintf("->赫夫曼树共分配了%d个结点空间,其中包含一个不用的0号单元\n", m + 1);//printf("->初始化所有叶子节点的权值,父亲和孩子:\n");for(p = HT + 1, i = 1; i <= n; ++i, ++p, ++w){p->weight = *w;p->parent = 0;//双亲初始值为0,表示此结点还没有被选择最小的算法选择过p->lchild = 0;//左右孩子初始化为0,表示空p->rchild = 0;}for(; i <= m; i++, p++){p->weight = 0;p->parent = 0;p->lchild = 0;p->rchild = 0;}for(i = n + 1; i <= m; ++i){Select(HT, i - 1, s1, s2);HT[s1].parent = i;//选出的两个权值最小的结点的双亲就是即将生成的结点HT[s2].parent = i;HT[i].lchild = s1;//即将生成的结点左孩子是s1,右孩子是s2,HT[i].rchild = s2;//因为s1比s2先选,所以s1总是小于等于s2HT[i].weight = HT[s1].weight + HT[s2].weight;//即将生成结点的权值就是两个权值最小的结点的权值之和}if(!(HC = (HuffmanCode)malloc((n + 1) * sizeof(char *)))){exit(OVERFLOW);}//ifif(!(cd = (char *)malloc(n * sizeof(char)))){exit(OVERFLOW);}cd[n-1] = '\0';for(int i = 1; i <= n; ++i){start = n - 1;for(int c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent){if(HT[f].lchild == c){cd[--start] = '0';}//ifelse //叶子结点根结点的右孩子{cd[--start] = '1';}//else}if(!(HC[i] = (char *)malloc((n - start) * sizeof(char)))){exit(OVERFLOW);}strcpy(HC[i], &cd[start]);//printf("->叶子节点%d的赫夫曼编码为:%s\n", i, HC[i]);}free(cd);return OK;}//HuffmanCodingvoid PrintHT(HuffmanTree HT, int n){int m = 2 * n - 1;printf("\n+-------------------------------------------------------------+\n");printf("| 赫夫曼树HT的存储结构|\n");printf("+----------+----------+----------+--------------+-------------+\n");printf("| 结点编号| weight | parent | leftchild | rightchild |\n");printf("+----------+----------+----------+--------------+-------------+\n");for(int i = 1; i <= m; i++){printf("| %4d | %4d | %4d | %4d | %4d |\n",i, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);printf("+----------+----------+----------+--------------+-------------+\n");}}for(int i = 0; i < len; i++){if(str[i] == 'A')a[65]++;else if(str[i] == 'B')a[66]++;else if(str[i] == 'C')a[67]++;else if(str[i] == 'D')a[68]++;else if(str[i] == 'E')a[69]++;else if(str[i] == 'F')a[70]++;else if(str[i] == 'G')a[71]++;else if(str[i] == 'H')a[72]++;else if(str[i] == 'I')a[73]++;else if(str[i] == 'J')a[74]++;else if(str[i] == 'K')a[75]++;else if(str[i] == 'L')a[76]++;else if(str[i] == 'M')a[77]++;else if(str[i] == 'N')a[78]++//部分统计字母代码主程序首先随机输入一篇英文文章,再统计各字母个数,再调用HuffmanCoding(HT, HC, w, n)函数,此函数又会调用void Select(HuffmanTree HT, int i, int &s1, int &s2)函数,而此函数又会调用int min(HuffmanTree HT, int i)函数,构造完成后便调用PrintHT(HT,n)函数输出赫夫曼树的存储结构。
哈夫曼树和哈夫曼编码
哈夫曼树和哈夫曼编码当树中的节点被赋予一个表示某种意义的数值,我们称之为该节点的权。
从树的根节点到任意节点的路径长度(经过的边数)与该节点上权值的乘积称为该节点的带权路径长度。
树中所有叶节点的带权路径长度之和称为该树的带权路径长度(WPL)。
当带权路径长度最小的二叉树被称为哈夫曼树,也成为最优二叉树。
如下图所示,有三课二叉树,每个树都有四个叶子节点a,b,c,d,分别取带权7,5,2,4。
他们的带权路径长度分别为(a) WPL = 7x2+5x2+2x2+4x2=36(b) WPL = 2X1+4X2+7X3+5X3 = 46(c) WPL = 7x1+5x2+2x3+4x3 = 35节点如果像c中的方式分布的话,WPL能取最小值(可证明),我们称为哈夫曼树。
哈夫曼树构造哈夫曼树在构造时每次从备选节点中挑出两个权值最小的节点进行构造,每次构造完成后会生成新的节点,将构造的节点从备选节点中删除并将新产生的节点加入到备选节点中。
新产生的节点权值为参与构造的两个节点权值之和。
举例如下:•备选节点为a,b,c,d,权值分别为7,5,2,4•选出c和d进行构造(权值最小),生成新节点为e(权值为6),备选节点变为7,5,6•选出b和e进行构造,生成新节点f(权值为11),备选节点为7,11 •将最后的7和11节点进行构造,最后生成如图所示的哈夫曼树哈夫曼树应用在处理字符串序列时,如果对每个字符串采用相同的二进制位来表示,则称这种编码方式为定长编码。
若允许对不同的字符采用不等长的二进制位进行表示,那么这种方式称为可变长编码。
可变长编码其特点是对使用频率高的字符采用短编码,而对使用频率低的字符则采用长编码的方式。
这样我们就可以减少数据的存储空间,从而起到压缩数据的效果。
而通过哈夫曼树形成的哈夫曼编码是一种的有效的数据压缩编码。
如果没有一个编码是另一个编码的前缀,则称这样的编码为前缀编码。
如0,101和100是前缀编码。
数据结构 第七章 哈夫曼树和哈夫曼编码
第七节 哈夫曼树和哈夫曼编码
授课教师: 胡 海 Mail: hh_whu@
第七节 哈夫曼树和哈夫曼编码
一、Huffman树 二、Huffman编码
Huffman树 Huffman编码
最优二叉树 不等长编码
带权路径 长度最短 的树
是通信 中最经典
的压缩编
A --- 1
B --- 00 C --- 011 D --- 0101 E --- 0100
5
0
10
A
1 1
假设每种字符在电文中出现的次数为wi,其 编码长度为li,电文中只有n种字符,则电文总 长为
0 B
0 C
i=1
带权路径长度。
wili
n
n
1 D
若置wi为叶子结点的权,li恰为从根到叶子的路径长度。则
恰为二叉树上
i=1
wili
利用哈夫曼树可以构造一种不等长的二进制编码,并且构造所得的哈夫曼编码 是一种最优前缀编码,即使所传电文的总长度最短。
频度高的信息用短码,
令d=0;i=10,a=110,n=111,则: WPL2=1bit×7+2bit×5+3bit×(2+4)=35
例:假设字符A,B,C,D,E的出现概率为42, 28, 15, 10, 5。求电文的总长度最短的一种编码?
首先构建哈夫曼树(根据概率) 0 58 0 28 1 30 0 15 1 15 1 100 1 42
哈夫曼树
100 42 23 11 19 8 29 14 58 29 15
3
5
7
8
哈夫曼树的应用1——哈夫曼编码
在电文传输中,需要将电文中出现的每个字符进行二进 制编码。在设计编码时需要遵守两个原则: (1)发送方传输的二进制编码,到接收方解码后必须 具有唯一性,即解码结果与发送方发送的电文完全一样; (2)发送的二进制编码尽可能地短。
数据结构——哈夫曼(Huffman)树+哈夫曼编码
数据结构——哈夫曼(Huffman)树+哈夫曼编码前天acm实验课,⽼师教了⼏种排序,抓的⼀套题上有⼀个哈夫曼树的题,正好之前离散数学也讲过哈夫曼树,这⾥我就结合课本,整理⼀篇关于哈夫曼树的博客。
哈夫曼树的介绍Huffman Tree,中⽂名是哈夫曼树或霍夫曼树,它是最优⼆叉树。
定义:给定n个权值作为n个叶⼦结点,构造⼀棵⼆叉树,若树的带权路径长度达到最⼩,则这棵树被称为哈夫曼树。
这个定义⾥⾯涉及到了⼏个陌⽣的概念,下⾯就是⼀颗哈夫曼树,我们来看图解答。
(01) 路径和路径长度定义:在⼀棵树中,从⼀个结点往下可以达到的孩⼦或孙⼦结点之间的通路,称为路径。
通路中分⽀的数⽬称为路径长度。
若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
例⼦:100和80的路径长度是1,50和30的路径长度是2,20和10的路径长度是3。
(02) 结点的权及带权路径长度定义:若将树中结点赋给⼀个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
例⼦:节点20的路径长度是3,它的带权路径长度= 路径长度 * 权 = 3 * 20 = 60。
(03) 树的带权路径长度定义:树的带权路径长度规定为所有叶⼦结点的带权路径长度之和,记为WPL。
例⼦:⽰例中,树的WPL= 1*100 + 2*50 +3*20 + 3*10 = 100 + 100 + 60 + 30 = 290。
⽐较下⾯两棵树上⾯的两棵树都是以{10, 20, 50, 100}为叶⼦节点的树。
左边的树WPL=2*10 + 2*20 + 2*50 + 2*100 = 360 右边的树WPL=350左边的树WPL > 右边的树的WPL。
你也可以计算除上⾯两种⽰例之外的情况,但实际上右边的树就是{10,20,50,100}对应的哈夫曼树。
⾄此,应该堆哈夫曼树的概念有了⼀定的了解了,下⾯看看如何去构造⼀棵哈夫曼树。
哈夫曼树及编码
哈夫曼树及编码
哈夫曼树(Huffman Tree),又称最优二叉树,即Huffman算法的输出,指的是一棵二叉树,其叶子节点代表的是原来的n个字符。
它的性质:1、每个叶子结点的权值都不相同,且与编码的位数有关;2、权值较大的结点离根较近;3、每个结点(除根)都有不同的左右子结点。
哈夫曼编码(Huffman Coding),又称最优编码,是一种用统一的二进制编码方案将几种不同的字符转换成比特流(二进制字串)的一种编码方法。
常见的编码有直接编码(Unary Coding)、多项式编码(Polynomial Coding)和哈夫曼编码(Huffman Coding)。
它使用贪婪算法来构建最优二叉树,将最常出现的字符转换为更短的编码。
哈夫曼树及哈夫曼编码的运用范围
哈夫曼树及哈夫曼编码的运用范围
哈夫曼树及哈夫曼编码是一种常用的数据压缩算法,广泛应用于数据传输和储存领域。
其运用范围主要包括以下几个方面:
1. 文件压缩:通过使用哈夫曼编码,可以将文件中的冗余信息压缩,减小文件的体积,从而提高文件传输的效率和节省存储空间。
2. 图像压缩:哈夫曼编码可以对图像进行数据压缩,删除冗余信息,减小图像的文件大小,提高图像传输速度和存储效率。
3. 音频压缩:哈夫曼编码常被用于音频数据的压缩,去除冗余数据,减小音频文件的大小,使其更易于传输和存储。
4. 视频压缩:哈夫曼编码也可以应用于视频数据的压缩,通过对视频中像素值的编码压缩,减小视频文件的体积,提高传输速度和存储效率。
5. 网络传输:在网络通信中,哈夫曼编码可以提高数据的传输效率,减小传输的数据量,提高网络的带宽利用率。
总之,哈夫曼树及哈夫曼编码在数据压缩和处理领域具有广泛的应用,可以减小文件、图像、音频和视频的体积,提高传输效率和存储效率。
赫夫曼树及其编码
2)树的带权路径长度为 树中所有叶子结点的带权 路径长度之和,通常记作
WPF=∑wKLK
k=1
n
最优二叉树或赫夫曼树:假设有n个权值 {ω ,ω ,…ω },试构造一棵有n个叶子结点的二 叉树,每个叶子结点带权为ω ,则其中带权路径 长度WPF最小的二叉树称做最优二叉树 或赫夫曼树。
赫夫曼树 6
现有一棵二叉树,它有四个叶子结点a、b、 c、d,分别带权7、5、2、4,你会计算它的带权 路径长度吗?
//为赫夫曼树的存储分配空间
for (p=HT,i=1;i<=n;++i,++p,++w) *p={ *w,0,0,0}; for (;i<=m;++i;++p) *p={0;0;0;0}; for (i=n+1;i<=m;++i) { //建赫夫曼树,在HT[1...i-1]选择parent
//为0且weight最小的两个结点,其序号分别为s1和s2。
最优二叉树(赫夫曼树)
15
5
7
Powerpoint2000版本
版本选择
赫夫曼树
PowerpointXP版本
1
你设计过将百分制转化成五级分制的程序吗?
a是一个百分制分数 If (a<60) b=“不及格”; else if (a<70) b=“及格”; else if (a<80) b=“中等”;
else if (a<90) b=“良好”;
本课小结
通过本课的学习,你需要掌握以下知识要点: 1. 了解、掌握基本概念,例如:路径长度、树的路 径长度、树的带权路径长度、最优二叉树(赫夫曼 树)。 2. 会计算任意给定的带权二叉树的带权路径长度。 3. 了解、掌握赫夫曼算法。 4. 给定带权叶子结点,能构造赫夫曼树(最小二叉 树)。
哈夫曼树编码解码
哈夫曼树编码解码
今天咱们来玩一个超级有趣的编码解码游戏,这个游戏就和哈夫曼树有关哦。
想象一下,我们在一个神秘的魔法森林里,这里的每棵树都有神奇的力量。
哈夫曼树就是这样一棵特别的树。
那这个哈夫曼树和编码解码有啥关系呢?咱们先来说编码。
比如说,我们有好多小动物,有小兔子、小猴子、小松鼠。
我们想给它们传递消息,但是又不想让别人轻易知道。
我们就可以用哈夫曼树来创造一种特别的密码。
我们先看看每个小动物出现的次数。
假如小兔子经常出现,小松鼠出现的次数比较少。
那在哈夫曼树里,小兔子就会有一个比较短的编码,就像住在离树根比较近的地方;小松鼠因为出现少,它的编码就会长一点,就像住在离树根远一点的树枝上。
比如说小兔子的编码是0,小猴子是10,小松鼠是11。
那如果我们要传递消息说“小兔子和小猴子在一起”,我们就可以写成010。
这就是哈夫曼编码啦。
那解码呢?就像是我们收到了这个神秘的密码010,我们要根据之前的规则把它还原成小动物。
我们看到0,就知道是小兔子,看到10就知道是小猴子。
再讲个故事吧。
有个小魔法师,他要给远方的魔法伙伴传递他看到的魔法生物的信息。
他看到了好多魔法蝴蝶、魔法蜻蜓和魔法萤火虫。
魔法蝴蝶特别多,魔法萤火虫比较少。
他就用哈夫曼树做出了编码。
魔法蝴蝶是1,魔法蜻蜓是01,魔法萤火虫是00。
当他把看到的魔法生物写成1011的时候,他的伙伴收到这个密码,就根据规则知道是魔法蝴蝶、魔法蜻蜓和魔法萤火虫啦。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
哈夫曼树构造算法
1、给定一个具有n个权值{ w1,w2,………wn }的结点的集合
F = { T1,T2,………Tn }
2、 初始时,设集合 A = F。 3、 执行 i = 1 至 n -1 的循环,在每次循环时执行以下操作
从当前集合中选取权值最小、次最小的两个结点,以这两个 结点作为内部结点 bi 的左右儿子,bi 的权值为其左右儿子 权值之和。 在集合中去除这两个权值最小、次最小的结点,并将内部结 点bI 加入其中。这样,在集合A中结点个数便减少了一个。
第 9 页
2011年10月
计算机科学与工程学院 TCS.CQ
a(10), e(15), i(12), s(3), t(4), 空格(13), 换行(1)。
a 10 a 10 e 15 e 15 i 12 i 12 S 3 t 4 t 4 空格 13 S 3 8 a 10 e 15 i 12 空格 13 S 3
计算机科学与工程学院 TCS.CQ
2 5 i 12 空格 13 5 8 4
3 3 1 8 8 t 4 换行 1 a 10 e 15
3 3
1 8 8 4 S 3 t 4 换行 1
2 5 e 15 i 12 空格 13
3
S
a 10
《数据结构》课件
计算机科学与工程学院 TCS.CQ
第 12 页
哈夫曼编码 A 001
2011年10月 计算机科学与工程学院 TCS.CQ 第 7 页
前缀编码
字符只放在叶结点中,所以任何一个字符的编码 都不是同一字符集中另一个字符的编码的前缀。 利用赫夫曼树可以构造一种不等长的二进制编码, 并且构造所得的赫夫曼编码是一种最优前缀编码, 即使所传电文的总长度最短。 字符编码可以有不同的长度
前缀编码可被惟一解码
《数据结构》课件
计算机科学与工程学院 TCS.CQ
第8页
哈夫曼树是一棵最小 代价的二叉树,在这 棵树上,所有的字符 都包含在叶结点上。
要使得整棵树的代价 最小,显然权值大的 叶子应当尽量靠近树 根,权值小的叶子可 以适当离树根远一些。
对应的哈夫曼编码如下: 1:000 3:001 5:01 7:1
这样,在经过了n-1 次循环之后,集合A中只剩下了一个结 点,这个结点就是根结点。
《数据结构》课件 计算机科学与工程学院 TCS.CQ 第5页
给定权值w=(1,3,5,7)来构造一棵哈夫曼树 。
16 4 1 3 5 7 1 3 1 (a) (b) 5 7 4 3 (c) 9 5 1 7 4 3 (d) 9 5 7
计算机科学与工程学院 TCS.CQ
第 16 页
赫夫曼树的存储
在哈夫曼树中,每个要编码的元素是一个叶结点,其它
结点都是度数为2的节点 一旦给定了要编码的元素个数,由n0=n2+1可知哈夫曼 树的大小为2n-1 哈夫曼树可用一个大小为2n的数组来存储。0节点不用, 根存放在节点1。叶结点依次放在n+1到2n的位置 每个数组元素保存的信息:结点的数据、权值和父结点
第 20 页
赫夫曼树的构造算法
void CreateHT(HTNode ht[],int n) { int i,j,k,lnode,rnode; for (i=0;i<2*n-1;i++) for (i=n;i<2*n-1;i++) { min1=min2=32767; for (k=0;k<=i-1;k++) if (ht[k].parent==0) { /*未构造二叉树的结点中查找或者=-1*/ lnode=rnode=0; float min1,min2; ht[i].parent=ht[i].lchild=ht[i].rchild=0; /*所有结点的相关域置初值或者-1*/
和左右孩子的位置
《数据结构》课件
计算机科学与工程学院 TCS.CQ
第 17 页
用ht[]数组存放哈夫曼树,对于具有n个叶子结点的哈夫 曼树,总共有2n-1个结点。 其算法思路是: n个叶子结点只有data和weight域值,先将所有2n-1个结点 的 parent 、 lchild 和 rchild 域置为初值 -1( 或者 0) 。处理每个 非叶子结点ht[i](存放在ht[n]~ht[2n-2]中):从ht[0] ~ht[i-2] 中找出根结点 ( 即其 parent 域为 -1 或者为 0) 的最小的两个结 点 ht[lnode] 和 ht[rnode], 将 它 们 作 为 ht[i] 的 左 右 子 树,ht[lnode]和ht[rnode]的双亲结点置为ht[i],并且 ht[i].weight=ht[lnode].weight+ht[rnode].weight。 如此这样直到所有2n-1个非叶子结点处理完毕。
2011年10月
计算机科学与工程学院 TCS.CQ
第 21 页
根据哈夫曼树求对应的哈夫曼编码的算法如下:
void CreateHCode(HTNode ht[],HCode hcd[],int n) { int i,f,c; HCode hc; for (i=0;i<n;i++) /*根据哈夫曼树求哈夫曼编码*/
E 01
e i sp
I 10
S 00000 T 0001
a
t
Sp 11
Nl 00001
s
nl
总共用了146bit
《数据结构》课件 计算机科学与工程学院 TCS.CQ 第 13 页
题例: 已知权值 W={ 5, 6, 2, 9, 7 }
5 6
6 9
2 7
9 7
7
所有原始结点是有一个 根结点,五左右子树
hc.cd[hc.start--]='1'; c=f;f=ht[f].parent; } hc.start++; hcd[i]=hc; } /*start指向哈夫曼编码最开始字符*/ /*再对双亲结点进行同样的操作*/
}
2011年10月 计算机科学与工程学院 TCS.CQ 第 22 页
编码的产生
对每个结点,从叶子往根推进,是左枝加0,是右枝加1
构造一棵哈夫曼树的过程
2011年10月 计算机科学与工程学院 TCS.CQ 第 6 页
哈夫曼编码
具体构造方法如下:
设需要编码的字符集合为 {d1,d2,…,dn}, 各个字符在 电 文 中 出 现 的 次 数 集 合 为 {w1,w2,…,wn}, 以 d1,d2,…,dn作为叶结点 ,以 w1,w2,…,wn作为各根结点 到每个叶结点的权值构造一棵二叉树。规定哈夫曼 树中的左分支为0,右分支为1,则从根结点到每个叶结 点所经过的分支对应的 0和 1组成的序列便为该结点 对应字符的编码。 这样的编码称为哈夫曼编码。 哈夫曼编码的生成: 每个字符的编码是根节点到该 字符的路径;左枝为0,右枝为1
5 9
5
《数据结构》课件
2 13
7
2 6
7
第 14 页
计算机科学与工程学院 TCS.CQ
9
5
7
2 6
13
7 0 0 6 00 13 1 7 01 29 0 9 10 1 16 0 5 110 1
7
《数据结构》课件
计算机科学与工程学院 TCS.CQ
第 15 页
1 2 111
哈夫曼树结点的数据结构(类型)
值 权 父 左 右 2 3 58 33 25 18 8 1 4 8 1 9 2 5 4 6 4 5 10 a e i s t 4 5 sp nl 13 1 3 6 10 15 12 3 4 2 3 6
WPL
w l
i 1
n
i i
其中 ,n 表示叶子结点数 ,wi 和 li 分别表示叶子结点 ki 的权值 和根到 ki之间的路径长度 (即从叶结点到根结点的分支数 )。 具有最小带权路径长度的二叉树称为哈夫曼树,又称 之为最优二差树。
2011年10月 计算机科学与工程学院 TCS.CQ 第 2 页
{ hc.start=n;c=i; f=ht[i].parent;
while (f!=-1) { if (ht[f].lchild==c) /*循环直到无双亲结点即到达树根结点*/ /*当前结点是左孩子结点*/
hc.cd[hc.start--]='0';
else
/*当前结点是双亲结点的右孩子结点*/
/*构造哈夫曼树*/ /*或者-1*/
if (ht[k].weight<min1) { min2=min1; rnode=lnode; min1=ht[k].weight;lnode=k; }
else if (ht[k].weight<min2) { } /*if*/ ht[lnode].parent=i;ht[rnode].parent=i; ht[i].weight=ht[lnode].weight+ht[rnode].weight; ht[i].lchild=lnode;ht[i].rchild=rnode; } } min2=ht[k].weight;rnode=k; }
生成过程分析
a e 15 2 i 12 3 s 3 6 t 4 5 sp 13 3 nl 1 6
3
值 权 父 左 右 0 2 3 1 58 33 1 4 8 2 25 1 9 12 3 18 2 5 7 4 8 4 6 11 5 4 5 10 13 6
10 4
7
8
9
10
11
12
13
《数据结构》课件
计算机科学与工程学院 TCS.CQ
《数据结构》课件 计算机科学与工程学院 TCS.CQ