哈夫曼树的构造
- 格式:ppt
- 大小:1.02 MB
- 文档页数:11
哈夫曼树结点数和叶子结点数的关系哈夫曼树是一种用来压缩信息的数据结构,它的关键在于能够将出现频率高的字符用较短的编码表示,从而达到节省空间的目的。
在构造哈夫曼树时,我们需要知道它的结点数和叶子结点数之间的关系,这是本文将要探讨的问题。
一、哈夫曼树的定义哈夫曼树(Huffman Tree)又称最优二叉树,是一种带权路径长度最短的树。
它的构造基于贪心算法,通过不断合并权值最小的两个子树来构造出最终的哈夫曼树。
在哈夫曼树中,树的每一个叶子结点都对应着一个字符,并且权值越大的叶子结点距离根结点越近。
二、哈夫曼树的结点数对于给定的n个字符(字符的出现概率已知),构造哈夫曼树时所需要的结点数是2n-1个。
这个结论可以通过归纳法证明。
首先,当n=1时,哈夫曼树只有一个结点,结论成立。
其次,当n=2时,哈夫曼树有3个结点。
假设当n=k时结论成立,考虑当n=k+1时的情况。
我们先对n个字符进行排序,找出权值最小的两个字符,将它们的权值相加得到一个新的权值,并构造一棵新的子树。
这样就将原来的n 个字符缩减成了n-1个字符,并且引入了一个新的结点。
在接下来的n-2次合并中,每次都引入了一个新的结点,所以总共需要2n-3个结点来构造出这n个字符对应的哈夫曼树。
此时,还需要引入一个新的根结点,作为两个结点(n个字符对应的两个子树)的父结点,并将两个子树连接到这个新的根结点上。
因此,总结点数为2n-3+1=2n-2。
最后,将新的根结点变为虚拟结点,并将n个叶子结点作为其子结点,就得到了结点数为2n-1的哈夫曼树。
三、哈夫曼树的叶子结点数在哈夫曼树中,每个叶子结点都对应着一个字符,因此叶子结点数就等于字符的个数n。
这个结论显然成立。
综上所述,哈夫曼树的结点数和叶子结点数之间存在如下关系:- 结点数 = 2n-1- 叶子结点数 = n这个结论可以帮助我们更好地理解哈夫曼树的构造过程,并且可以在实际应用中帮助我们提前估算哈夫曼树的大小。
wpl怎么算
wpl怎么算:wpl=∑Wκ•lκ(n,i=1)
哈夫曼树的构造
每次把最小的两棵二叉树合并。
将数据储存在最小堆中,每次找出最小的并从最小堆中删除。
这其实是一个找出数列其中最小的两个数,作为叶子,然后从数列中删除这两个数。
用这两个数的和sum作为叶子的根,再将根值插入数列中。
其过程为:
1.在堆中找出最小两个数a,b
2.从数列中删除a,b。
并求和a+b=sum
3.建立哈夫曼树,a,b作为叶子结点,sum作为叶子的父节点
4.再将sum插入数列中
5.循环
给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)。
哈夫曼树也可以是k叉的,只是在构造k叉哈夫曼树时需要先进行一些调整。
构造哈夫曼树的思想是每次选k个权重最小的元素来合成一个新的元素,该元素权重为k个元素权重之和。
但是当k大于2时,按照这个步骤做下去可能到最后剩下的元素少于k个。
解决这个问题的办法是假设已经有了一棵哈夫曼树(且为一棵满k叉树),则可以计算出其叶节点数目为(k-1)nk+1,式子中的nk表示子节点数目为k的节点数目。
于是对给定的n个权值构造k叉哈夫曼树时,
可以先考虑增加一些权值为0的叶子节点,使得叶子节点总数为(k-1)nk+1这种形式,然后再按照哈夫曼树的方法进行构造即可。
6.3哈夫曼树6.3.1基本术语1.路径和路径长度若在一棵中存在着一个结点序列k1 ,k2,…,kj,使得ki是k1+i 的双亲(1ji<≤),则称此结点序列是从k1~kj的路径,因树中每个结点只有一个双亲结点,所以它也是这两个结点之间k 1~kj所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1(实际就是边数)。
如在图5-19(a)所示的二叉树中,从树根结点L到叶子结点P的路径为结点序列L、M、S、P,路径长度为3。
(a) (b)(c) (d)图5-19 二叉排序树的删除2.结点的权和带权路径长度在许多应用中,常常将树中的结点赋上一个有着某种意义的实数,我们称此实数为该结点的权。
结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘积3.树的带权路径长度树的带权路径长度定义为树中所有叶子结点的带权路径长度这和,通常记为:2 WPL = ∑=n i i i lw 1其中n 表示叶子结点的数目,i w 和i l 分别表示叶子结点i k 的权值和根到i k 之间的路径长度 。
4.哈夫曼树哈夫曼(Huffman)树又称最优二叉树。
它是n 个带权叶子结点构成的所有二叉树中,带权路径长度 WPL 最小的二叉树。
因为构造这种树的算法是最早由哈夫曼于1952年提出的,所以被称之为哈夫曼树。
例如,有四个叶子结点a 、b 、c 、d ,分别带权为9、4、5、2,由它们构成的三棵不同的二叉树(当然还有其它许多种)分别如图5-20(a)到图5-20(c)所示。
b ac a b cd d c a b d(a) (b) (c)图5-20 由四个叶子结点构成的三棵不同的带权二叉树 每一棵二叉树的带权路径长度WPL 分别为:(a) WPL = 9×2 + 4×2 + 5×2 + 2×2 = 40(b) WPL = 4×1 + 2×2 + 5×3 + 9×3 = 50(c) WPL = 9×1 + 5×2 + 4×3 + 2×3 = 37其中图5-20(c)树的WPL 最小,稍后便知,此树就是哈夫曼树。
哈夫曼树空指针域
哈夫曼树是一种数据结构,通常用于实现二叉搜索树的某些特定操作。
它是一种树状数据结构,由若干个节点组成,每个节点包含了两个子节点,这些节点也被称为左右子节点。
空指针域是哈夫曼树的一个非常重要的特征。
在普通二叉搜索树中,空指针域通常是空的,而在哈夫曼树中,空指针域不仅可以存储根节点,还可以存储若干个左子节点和若干个右子节点。
哈夫曼树的构建过程如下。
首先,将根节点作为哈夫曼树的根节点,然后将根节点的左子节点和右子节点都设为空。
接着,从根节点开始,依次将左子节点和右子节点设为空,并将当前节点设置为有意义的节点。
这个过程一直持续到所有节点都被设置为有意义的节点。
在哈夫曼树的左子树和右子树中,空指针域通常是成对出现的。
也就是说,在左子树中,空指针域通常是(根节点,空),而在右子树中,空指针域通常是(根节点,空)。
哈夫曼树的空指针域可以用来存储任意数量的左子节点和任意数量的右子节点。
通过这种方式,我们可以将哈夫曼树表示成一个二叉树结构,其中包含一个根节点和若干个左子节点和右子节点。
空指针域在哈夫曼树中的作用非常重要。
它可以帮助我们实现一些非常有趣的数据结构,如计数器、优先队列等。
此外,在实际应用中,空指针域也可以用来优化哈夫曼树的构建过程,从而提高搜索效率。
总之,哈夫曼树是一种非常有趣的数据结构,其空指针域可以帮助我们实现许多有用的功能。
第2章线性表1.选择题(1)顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是()。
A.110 B.108 C.100 D.120答案:B解释:顺序表中的数据连续存储,所以第5个元素的地址为:100+2*4=108。
(3)向一个有127个元素的顺序表中插入一个新元素并保持原来顺序不变,平均要移动的元素个数为()。
A.8 B.63.5 C.63 D.7答案:B解释:平均要移动的元素个数为:n/2。
(4)链接存储的存储结构所占存储空间()。
A.分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针B.只有一部分,存放结点值C.只有一部分,存储表示结点间关系的指针D.分两部分,一部分存放结点值,另一部分存放结点所占单元数答案:A(5)线性表若采用链式存储结构时,要求内存中可用存储单元的地址()。
A.必须是连续的B.部分地址必须是连续的C.一定是不连续的D.连续或不连续都可以答案:D(6)线性表L在()情况下适用于使用链式结构实现。
A.需经常修改L中的结点值B.需不断对L进行删除插入C.L中含有大量的结点D.L中结点结构复杂答案:B解释:链表最大的优点在于插入和删除时不需要移动数据,直接修改指针即可。
(7)单链表的存储密度()。
A.大于1 B.等于1 C.小于1 D.不能确定答案:C解释:存储密度是指一个结点数据本身所占的存储空间和整个结点所占的存储空间之比,假设单链表一个结点本身所占的空间为D,指针域所占的空间为N,则存储密度为:D/(D+N),一定小于1。
(8)将两个各有n个元素的有序表归并成一个有序表,其最少的比较次数是()。
A.n B.2n-1 C.2n D.n-1答案:A解释:当第一个有序表中所有的元素都小于(或大于)第二个表中的元素,只需要用第二个表中的第一个元素依次与第一个表的元素比较,总计比较n次。
(9)在一个长度为n的顺序表中,在第i个元素(1≤i≤n+1)之前插入一个新元素时须向后移动()个元素。
构造霍夫曼树的方法嘿,咱今儿就来聊聊构造霍夫曼树的那些事儿!你说这霍夫曼树啊,就像是一个奇妙的拼图游戏。
想象一下,你有一堆乱七八糟的小木块,每个木块上都标着不同的数字,这就好比是各种字符和它们出现的频率。
咱的任务呢,就是要把这些小木块巧妙地组合起来,搭建成一棵漂亮又实用的树。
首先呢,咱得把这些带着数字的小木块按照数字大小排个序。
这就像是给它们排排队,让它们整整齐齐的。
然后呢,从最小的两个木块开始,把它们组合成一个新的“大块头”,这个“大块头”的数字就是那两个小木块数字的和。
接下来,再把这个新的“大块头”放回队伍里,重新排序。
就这么不断地重复这个过程,把小的组合成大的,再组合成更大的。
你看啊,这多像我们搭积木呀!一块一块往上堆,最后堆出一个神奇的形状。
在构造霍夫曼树的过程中,可不能马虎哟!每一步都得小心翼翼,就跟走钢丝似的,稍有偏差,这棵树可能就长得不那么完美啦。
而且哦,你还得有耐心。
这可不是一下子就能完成的事儿,得慢慢来,就像绣花一样,一针一线都得精细。
等咱终于把这棵霍夫曼树构造好啦,哇塞,那可真是满满的成就感呀!就好像自己创造了一个小世界一样。
这时候再回头看看那些原本杂乱无章的小木块,现在都乖乖地在树上找到自己的位置啦,多有意思!构造霍夫曼树,不仅是一个技术活儿,更是一种乐趣呀!它让我们看到数字和算法的奇妙之处,让我们感受到知识的力量。
所以呀,别小瞧了这构造霍夫曼树的方法,它能在很多地方发挥大作用呢!比如在数据压缩里,它就能帮我们节省好多空间,让信息传递得更快更高效。
怎么样,是不是对构造霍夫曼树有了更深刻的认识啦?赶紧去试试吧,说不定你就能搭出一棵超级棒的霍夫曼树呢!。
哈夫曼树的构造代码x#include<stdio.h>#include<malloc.h>//定义哈夫曼树节点typedef struct{tint weight;//权值tint lchild,rchild,parent;//子节点和双亲节点序号}HTNode;//定义哈夫曼树typedef struct{tHTNode *nodes;//节点数组tint length;//树的节点数}HuffmanTree;//创建哈夫曼树HuffmanTree *createHuffmanTree(int *set,int length){tint m,i,j,x1,x2;//m表示总节点数,x1和x2记录权值最小的两个节点tHuffmanTree *tree;tHTNode *node;tm=2*length-1;t//申请树的节点数组ttree=(HuffmanTree*)malloc(sizeof(HuffmanTree));tnode=(HTNode*)malloc(m*sizeof(HTNode));t//初始化tfor(i=0;i<m;i++)t{ttnode[i].parent=-1;ttnode[i].lchild=-1;ttnode[i].rchild=-1;ttif(i<length)tt{tttnode[i].weight=set[i];tt}ttelsetttnode[i].weight=0;t}t//建立哈夫曼树tfor(i=length;i<m;i++)t{tt//x1表示权值最小的节点序号,x2表示次最小节点的序号ttx1=0;ttx2=0;tt//找出权值最小的节点ttfor(j=0;j<i;j++)tt{tttif(node[j].parent==-1)ttt{ttttif((x1==0)||node[j].weight<node[x1].weight)tttt{tttttx1=j;tttt}ttt}tt}tt//找出权值次最小的节点ttfor(j=0;j<i;j++)tt{tttif(node[j].parent==-1)ttt{ttttif((x2==0)||node[j].weight<node[x2].weight&&x1!=j) tttt{tttttx2=j;tttt}ttt}tt}tt//建立一个新的节点ttnode[i].weight=node[x1].weight+node[x2].weight; ttnode[i].lchild=x1;ttnode[i].rchild=x2;ttnode[x1].parent=i;ttnode[x2].parent=i;t}ttree->nodes=node;ttree->length=m;treturn tree;}//打印哈夫曼树void printHuffmanTree(HuffmanTree *tree){tint i;tprintf('序号t权值t双亲t左孩子t右孩子');tfor(i=0;i<tree->length;i++)t{ttprintf('%dt%dt%dt%dt%d',i,tree->nodes[i].weight,tree->nodes[i].parent, ttttree->nodes[i].lchild,tree->nodes[i].rchild); t}}//释放哈夫曼树void clearHuffmanTree(HuffmanTree *tree){tif(tree==NULL)ttreturn;tfree(tree->nodes);tfree(tree);}int main(){tHuffmanTree *tree;tint set[7]={5,29,7,8,14,23,3};t//创建哈夫曼树ttree=createHuffmanTree(set,7);t//打印哈夫曼树tprintHuffmanTree(tree);t//释放哈夫曼树tclearHuffmanTree(tree);treturn 0; }。
求哈夫曼树的带权路径长度
哈夫曼树,即最优二叉树,是根据哈夫曼编码和贪心策略构造的一种特殊的二叉树。
哈夫曼树的叶节点按照权值增大的原则进行排序,将每一层次上(叶节点外)的结点都作为父节点,由上至下构造树的过程称为哈夫曼树的构造。
建立一棵哈夫曼树需要经历若干步骤:
1. 给定n个权值作为n个叶节点,构造只含n个叶节点的二叉树。
2. 在这棵树的n-1个非终端结点中,选择两个最小的权值 Wi和Wj,构造一个新的二叉树,把Wi和Wj替换为一个新的结点Wij,Wij的权值是Wi+Wj。
3. 重复上述第二步,不断构造新的二叉树,直到所有结点中只有一个结点,即哈夫曼树构建完成。
哈夫曼树的带权路径长度即树中所有叶子节点和根节点之间的路径长度,记作WPL (Weighted Path Length),根据下式可以计算WPL:
WPL=∑i=1nWi*Di
其中Wi是第i个叶子节点的权值,而Di是第i个叶子节点到根节点的路径长度。
所以,求哈夫曼树的带权路径长度的关键是求出每个叶子到根结点的路径长度。
在求哈夫曼树的带权路径长度时,通常采用从上至下,从左往右的思想,按照层次遍历哈夫曼树,逐个计算每一个叶子节点到根节点的路径长度,再求和得到带权路径长度。
具体来说,首先给根节点的路径长度赋初值为0,然后依次遍历每个叶子节点,当前叶子结点的路径长度等于它的父节点的路径长度加上1(因为有一条路径),然后累加得到带权路径长度。
因此,求哈夫曼树的带权路径长度,可以采用从上至下,从左往右遍历哈夫曼树,计算每一个叶子节点到根节点的路径长度,再求和得到带权路径长度。
第1章绪论1 •简述下列概念:数据、数据元素、数据项、数据对象、数据结构、逻辑结构、存储结构、抽象数据类型。
答案:数据:是客观事物的符号表示,指所有能输入到计算机中并被计算机程序处理的符号的总称。
如数学计算中用到的整数和实数,文本编辑所用到的字符串,多媒体程序处理的图形、图像、声音、动画等通过特殊编码定义后的数据。
数据元素:是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。
在有些情况下,数据元素也称为元素、结点、记录等。
数据元素用于完整地描述一个对象,如一个学生记录,树中棋盘的一个格局(状态)、图中的一个顶点等。
数据项:是组成数据元素的、有独立含义的、不可分割的最小单位。
例如,学生基本信息表中的学号、姓名、性别等都是数据项。
数据对象:是性质相同的数据元素的集合,是数据的一个子集。
例如:整数数据对象是集合N={0, ± 1,土2,…},字母字符数据对象是集合C={ ‘A',‘ B',…,‘ Z', ‘a',‘ $,•••,‘ z' }, 学生基本信息表也可是一个数据对象。
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。
换句话说,数据结构是带“结构”的数据元素的集合,“结构”就是指数据元素之间存在的关系。
逻辑结构:从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。
因此,数据的逻辑结构可以看作是从具体问题抽象出来的数学模型。
存储结构:数据对象在计算机中的存储表示,也称为物理结构。
抽象数据类型:由用户定义的,表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称。
具体包括三部分:数据对象、数据对象上关系的集合和对数据对象的基本操作的集合。
2•试举一个数据结构的例子,叙述其逻辑结构和存储结构两方面的含义和相互关系。
答案:例如有一张学生基本信息表,包括学生的学号、姓名、性别、籍贯、专业等。
每个学生基本信息记录对应一个数据元素,学生记录按顺序号排列,形成了学生基本信息记录的线性序列。
第八节最优二叉树(哈夫曼树)一、概念在具有n个带权叶结点的二叉树中,使所有叶结点的带权路径长度之和(即二叉树的带权路径长度)为最小的二叉树,称为最优二叉树(又称最优搜索树或哈夫曼树),即最优二叉树使(W k—第k个叶结点的权值;P k—第k个叶结点的带权路径长度)达到最小。
二、最优二叉树的构造方法假定给出n个结点ki(i=1‥n),其权值分别为Wi(i=1‥n)。
要构造以此n个结点为叶结点的最优二叉树,其构造方法如下:首先,将给定的n个结点构成n棵二叉树的集合F={T1,T2,……,Tn}。
其中每棵二叉树Ti中只有一个权值为wi的根结点ki,其左、右子树均为空。
然后做以下两步⑴在F中选取根结点权值最小的两棵二叉树作为左右子树,构造一棵新的二叉树,并且置新的二叉树的根结点的权值为其左、右子树根结点的权值之和;⑵在F中删除这两棵二叉树,同时将新得到的二叉树加入F 重复⑴、⑵,直到在F中只含有一棵二叉树为止。
这棵二叉树便是最优二叉树。
三、最优二叉树的数据类型定义在最优二叉树中非叶结点的度均为2,为满二叉树,因此采用顺序存储结构为宜。
如果带权叶结点数为n个,则最优二叉树的结点数为2n-1个。
Const n=叶结点数的上限;m=2*n-1;{最优二叉树的结点数}Typenode=record{结点类型}data:<数据类型>;{权值}prt,lch,rch,lth:0‥m;{父指针、左、右指针和路径长度}end;wtype=array[1‥n] of <数据类型> ;{n个叶结点权值的类型}treetype=array[1‥m] of node;{最优二叉树的数组类型}Var tree:treetype;{其中tree [1‥n]为叶结点,tree [n+1‥2n-1]为中间结点,根为tree [2n-1]}四、构造最优二叉树的算法。
数据结构——哈夫曼(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}对应的哈夫曼树。
⾄此,应该堆哈夫曼树的概念有了⼀定的了解了,下⾯看看如何去构造⼀棵哈夫曼树。
数据结构与算法:哈夫曼树哈夫曼树给定N个权值作为N个叶⼦结点,构造⼀棵⼆叉树,若该树的带权路径长度达到最⼩,称这样的⼆叉树为最优⼆叉树,也称为哈夫曼树(Huffman Tree)。
哈夫曼树是带权路径长度最短的树,权值较⼤的结点离根较近。
重要概念路径:从⼀个节点到它往下可以达到的节点所经shu过的所有节点,称为两个节点之间的路径路径长度:即两个节点的层级差,如A节点在第⼀层,B节点在第四层,那它们之间的路径长度为4-1=3权重值:为树中的每个节点设置⼀个有某种含义的数值,称为权重值(Weight),权重值在不同算法中可以起到不同的作⽤节点的带权路径长度:从根节点到该节点的路径长度与该节点权重值的乘积树的带权路径长度:所有叶⼦节点的带权路径长度之和,也简称为WPL哈夫曼树判断判断⼀棵树是不是哈夫曼树只要判断该树的结构是否构成最短带权路径。
在下图中3棵同样叶⼦节点的树中带权路径最短的是右侧的树,所以右侧的树就是哈夫曼树。
代码实现案例:将数组{13,7,8,3,29,6,1}转换成⼀棵哈夫曼树思路分析:从哈夫曼树的概念中可以看出,要组成哈夫曼树,权值越⼤的节点必须越靠近根节点,所以在组成哈夫曼树时,应该由最⼩权值的节点开始。
步骤:(1) 将数组转换成节点,并将这些节点由⼩到⼤进⾏排序存放在集合中(2) 从节点集合中取出权值最⼩的两个节点,以这两个节点为⼦节点创建⼀棵⼆叉树,它们的⽗节点权值就是它们的权值之和(3) 从节点集合中删除取出的两个节点,并将它们组成的⽗节点添加进节点集合中,跳到步骤(2)直到节点集合中只剩⼀个节点public class HuffmanTreeDemo {public static void main(String[] args) {int array[] = {13,7,8,3,29,6,1};HuffmanTree huffmanTree = new HuffmanTree();Node root = huffmanTree.create(array);huffmanTree.preOrder(root);}}//哈夫曼树class HuffmanTree{public void preOrder(Node root){if (root == null){System.out.println("哈夫曼树为空,⽆法遍历");return;}root.preOrder();}/*** 创建哈夫曼树* @param array 各节点的权值⼤⼩* @return*/public Node create(int array[]){//先将传⼊的各权值转成节点并添加到集合中List<Node> nodes = new ArrayList<>();for (int value : array){nodes.add(new Node(value));}/*当集合中的数组只有⼀个节点时,即集合内所有节点已经组合完成,剩下的唯⼀⼀个节点即是哈夫曼树的根节点*/while (nodes.size() > 1){//将节点集合从⼩到⼤进⾏排序//注意:如果在节点类没有实现Comparable接⼝,则⽆法使⽤Collections.sort(nodes);//在集合内取出权值最⼩的两个节点Node leftNode = nodes.get(0);Node rightNode = nodes.get(1);//以这两个节点创建⼀个新的⼆叉树,它们的⽗节点的权值即是它们的权值之和Node parent = new Node(leftNode.weight + rightNode.weight);parent.left = leftNode;parent.right = rightNode;//再从集合中删除已经组合成⼆叉树的俩个节点,并把它们俩个的⽗节点加⼊到集合中nodes.remove(leftNode);nodes.remove(rightNode);nodes.add(parent);}//返回哈夫曼树的根节点return nodes.get(0);}}//因为要在节点的集合内,以节点的权值value,从⼩到⼤进⾏排序,所以要实现Comparable<>接⼝class Node implements Comparable<Node>{int weight;//节点的权值Node left;Node right;public Node(int weight) {this.weight = weight;}public void preOrder(){System.out.println(this);if (this.left != null){this.left.preOrder();}if (this.right != null){this.right.preOrder();}}@Overridepublic String toString() {return "Node{" +"weight=" + weight +'}';}@Overridepublic int compareTo(Node o) {return this.weight - o.weight;}}哈夫曼编码定长编码固定长度编码⼀种⼆进制信息的信道编码。
哈夫曼树的构造过程咱们今天来聊聊一个挺有意思的东西——哈夫曼树,听起来挺高大上的,其实啊,它就像咱们日常生活中搭积木,只不过这个积木是用数字和字符搭起来的。
首先,你得有一堆数字或者字符,它们就像是你的“积木块”,每个块上都有个“重量”,这个重量在哈夫曼树里就是它们出现的频率。
你想啊,如果某个字符在文章里经常出现,那它就像个沉甸甸的大积木,对吧?接下来,咱们就开始搭积木了。
第一步,你得把这些积木块分成两堆,尽量让两堆的重量差不多。
这就像是你手里拿着一堆大小不一的苹果,你想把它们分成两篮,尽量让两篮的苹果重量差不多。
这个过程,咱们就叫它“初始化”。
好,现在咱们有了两堆积木了。
接下来,咱们就从这两堆积木里各挑出一个最轻的积木来,把它们放在一起,用一根小棍子给它们“焊接”起来,成了一个新的积木块。
这个新积木块的重量,就是它两个组成积木块的重量之和。
这就像是你把两个小的苹果绑在一起,做成了一个大苹果。
然后,咱们再把这个新积木块放回原来的积木堆里,和其他的积木块一起。
这时候,你手上的积木块就少了一个,但是出现了一个更重的积木块。
接下来,咱们就重复上面的步骤:再从两堆积木里各挑出一个最轻的,焊接起来,再放回原来的积木堆里。
这个过程,就像是你在不断地找苹果,绑苹果,然后再放回篮子里。
每一次,你都在减少积木块的数量,但是每次都会诞生一个更重的积木块。
这就像是你手上的苹果越来越少,但是每个苹果都越来越大。
终于,当所有的积木块都焊接成了一个大积木块的时候,这个大积木块就是咱们要找的哈夫曼树了。
它就像一个巨大的苹果树,每个树枝都是一个小苹果组成的,而整棵树则是由所有的小苹果一起组成的。
你可能会问,这哈夫曼树到底有啥用啊?其实啊,它有很多用处,比如在数据压缩中,哈夫曼树可以帮助我们把经常出现的字符用更短的编码来表示,这样文件就能变得更小,传输起来也就更快了。
所以你看,这哈夫曼树虽然听起来挺高大上的,但其实它就像咱们日常生活中的搭积木一样简单。