问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

数据结构—哈夫曼树和哈夫曼编码介绍以及Java实现案例

发布网友 发布时间:2024-09-27 09:17

我来回答

1个回答

热心网友 时间:2024-10-04 05:28

哈夫曼树原本是为哈夫曼编码服务的一种数据结构,又称最优二叉树,哈夫曼编码常被使用在数据的压缩和解压缩技术之中。本文详细介绍了哈夫曼树的概念,并且提供了Java实现,最后又介绍了哈夫曼编码。

1 哈夫曼树1.1 哈夫曼树简介

哈夫曼树:给定N个权值作为N个叶子节点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

以下图为例,先说明几个概念:

权:

赋予某个实体的一个量,是对实体的某个或某些属性的数值描述。在数据结构中,实体有节点(元素)和边(关系)两大类,所以对应有节点权和边权。

路径:

在一棵树中,一个结点到另一个结点之间的通路,称为路径。上图,从根结点到结点 h之间的通路就是一条路径。

节点路径长度:

在一棵树中,从一个节点到另一个节点所经过的“边”的数量,被我们称为两个结点之间的路径长度;或者说路径上的分支数目称作路径长度。上图中从根结点到结点 h 的路径长度为3。

树的路径长度:

树的路径长度就是从树根到每一结点的路径长度之和。上图的树的路径长度为1+2+3+3+2+3+1+2+2+3=22

节点的带权路径长度:

树的每一个结点,都可以拥有自己的“权重”(Weight),权重在不同的算法当中可以起到不同的作用。结点的带权路径长度,是指树的根结点到该结点的路径长度,和该结点权重的乘积。上图中h节点带权路径长度为3 * 3=9

树的带权路径长度:

树中所有叶子结点的带权路径长度之和。也被简称为WPL。上图的树的带权路径长度为32+33+34+21+3 * 5=44

1.2 哈夫曼算法

哈夫曼树的构建方法被称为哈夫曼算法,其构建步骤为:

根据给定的n个权值{w1,w2,...,wn}构成n棵二叉树的集合F={T1,T2,...,Tn},其中每棵二叉树Ti中只有一个带权为wi根结点,其左右子树均为空。

在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。

在F中删除这两棵树,同时将新得到的二叉树加入F中。

重复2和3步骤,直到F只含一棵树为止。这棵树便是哈夫曼树。

注意:

叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。

最优二叉树中,权越大的叶子离根越近。

最优二叉树的WPL最小,但是形态不唯一。

1.3 案例

有五个带权节点{ A5,B15,C40,D30,E10}。要求构建哈夫曼树。

先把有权值的叶子结点按照从小到大的顺序排列成一个有序序列,即:A5,E10,B15,D30,C40。

取头两个最小权值的结点作为一个新节点N1的两个子结点,取相对较小的是左孩子(实际上也可以不遵守,因为哈夫曼树是有多种形状的,但是WPL都是最小的),这里就是A为N1的左孩子,E为N1的右孩子,如下图,新结点的权值为两个叶子权值的和5+10=15。

将N1替换A与E,插入有序序列中,保持从小到大排列。即:N115,B15,D30,C40。

重复步骤2。将N1与B作为一个新节点N2的两个子结点。如下图,N2的权值=15+15=30。

将N2替换N1与B,插入有序序列中,保持从小到大排列。即:N230,D30,C40。

重复步骤2。将N2与D作为一个新节点N3的两个子结点。如下图,N3的权值=30+30=60。

将N3替换N2与D,插入有序序列中,保持从小到大排列。即:C40,N360。

重复步骤2。将C与N3作为一个新节点T的两个子结点,如下图,由于T即是根结点,完成哈夫曼树的构造。

此时的上图二叉树的带权路径长度WPL=40×1+30×2+15×3+10×4+5×4=205。这就是最短带权路径长度。

根据最优二叉树形态不唯一的性质和上面的一种形状,我们还可以写出下面形状的最优二叉树:

2 哈夫曼树的实现

现提供哈夫曼树的Java实现,下面的类提供了两种构建方法,区别主要在一个排序使用Arrays.sort排序(注意由于是对象排序,它并非双轴快排),另一个使用了最小堆排序。它们的时间效率是不一致的。 实际使用时选取一种即可。

/***哈夫曼树简单实现*/publicclassHuffmanTree<E>{/***外部保存根节点的引用*/privateBinaryTreeNode<E>root;/***内部节点**@param<E>节点数据类型*/publicstaticclassBinaryTreeNode<E>{//节点数据Edata;//节点权重Stringweight;//左子结点BinaryTreeNode<E>left;//右子节点BinaryTreeNode<E>right;publicBinaryTreeNode(Edata,Stringweight){this.data=data;this.weight=weight;}@OverridepublicStringtoString(){return"Node{"+"data="+data+",weight='"+weight+'\''+'}';}}/***根据指定的普通node节点集合构建哈夫曼树**@parambinaryTreeNodesnode节点集合,采用普通list集合*@param<E>节点数据类型*@return哈夫曼树*/publicstatic<E>HuffmanTree<E>build(List<BinaryTreeNode<E>>binaryTreeNodes){//比较器Comparator<BinaryTreeNode<E>>comparator=(o1,o2)->newBigDecimal(o1.weight).subtract(newBigDecimal(o2.weight)).intValue();while(binaryTreeNodes.size()>1){//手动对集合的节点按照权值大小从大到小进行排序binaryTreeNodes.sort(comparator);//移除并获取权值最小的两个节点BinaryTreeNode<E>left=binaryTreeNodes.remove(0);BinaryTreeNode<E>right=binaryTreeNodes.remove(0);//生成新节点,新节点的权值为两个子节点的权值之和BinaryTreeNode<E>parent=newBinaryTreeNode<>(null,newBigDecimal(left.weight).add(newBigDecimal(right.weight)).toString());//让新节点作为两个权值最小节点的父节点parent.left=left;parent.right=right;//将新节点加入到集合中binaryTreeNodes.add(parent);}//采用循环不断地执行上面的步骤,直到list集合中只剩下一个节点,最后剩下的这个节点就是哈夫曼树的根节点HuffmanTree<E>huffmanTree=newHuffmanTree<>();huffmanTree.root=binaryTreeNodes.remove(0);returnhuffmanTree;}/***根据指定的最小堆构建哈夫曼树**@parambinaryTreeNodesnode节点集合,采用最小堆*@param<E>节点数据类型*@return哈夫曼树*/publicstatic<E>HuffmanTree<E>build(PriorityQueue<BinaryTreeNode<E>>binaryTreeNodes){while(binaryTreeNodes.size()>1){//移除并获取权值最小的两个节点BinaryTreeNode<E>left=binaryTreeNodes.poll();BinaryTreeNode<E>right=binaryTreeNodes.poll();//生成新节点,新节点的权值为两个子节点的权值之和BinaryTreeNode<E>parent=newBinaryTreeNode<>(null,newBigDecimal(left.weight).add(newBigDecimal(right.weight)).toString());//让新节点作为两个权值最小节点的父节点parent.left=left;parent.right=right;//将新节点加入到最小堆中,自动排序binaryTreeNodes.add(parent);}//采用循环不断地执行上面的步骤,直到list集合中只剩下一个节点,最后剩下的这个节点就是哈夫曼树的根节点HuffmanTree<E>huffmanTree=newHuffmanTree<>();huffmanTree.root=binaryTreeNodes.poll();returnhuffmanTree;}/***获取根节点**@return根节点或者null-表示空树*/publicBinaryTreeNode<E>getRoot(){returnroot;}}2.1 测试publicclassHuffmanTreeTest{/*采用用普通集合和最小堆都行,最大的区别是它们的采用不同的排序算法,效率是不一致的*//***采用普通list作为临时存储节点数据的集合,因此我们需要手动排序*/@Testpublicvoidtest1(){//采用普通list作为临时存储节点数据的集合,因此我们需要手动排序List<BinaryTreeNode<String>>binaryTreeNodes=newArrayList<>();//A5,B15,C40,D30,E10binaryTreeNodes.add(newBinaryTreeNode<>("A","5"));binaryTreeNodes.add(newBinaryTreeNode<>("B","15"));binaryTreeNodes.add(newBinaryTreeNode<>("C","40"));binaryTreeNodes.add(newBinaryTreeNode<>("D","30"));binaryTreeNodes.add(newBinaryTreeNode<>("E","10"));HuffmanTree<String>huffmanTree=HuffmanTree.build(binaryTreeNodes);BinaryTreeNode<String>root=huffmanTree.getRoot();System.out.println(root);}/***采用最小堆--priorityQueue作为临时存储节点数据的集合,priorityQueue的性质就是对集合的元素进行自动排序,因此我们不必手动排序*/@Testpublicvoidtest2(){//采用最小堆--priorityQueue.作为临时存储节点数据的集合,priorityQueue的性质就是对集合的元素进行自动排序,我们只需要指定排序规则PriorityQueue<BinaryTreeNode<String>>priorityQueueBinaryTreeNodes=newPriorityQueue<>((o1,o2)->newBigDecimal(o1.weight).subtract(newBigDecimal(o2.weight)).intValue());priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("A","5"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("B","15"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("C","40"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("D","30"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("E","10"));HuffmanTree<String>huffmanTree=HuffmanTree.build(priorityQueueBinaryTreeNodes);BinaryTreeNode<String>root=huffmanTree.getRoot();System.out.println(root);}}3 哈夫曼编码

哈夫曼树的出现主要是为了哈夫曼编码服务的,哈夫曼编码有着非常广泛的应用,哈夫曼编码常被使用在数据的压缩和解压缩技术之中。

哈夫曼编码的基本思想是以字符的使用频率作为权,构造一棵哈夫曼树,然后利用哈夫曼树对字符进行编码。这棵哈夫曼树,是将所要编码的字符作为叶子结点,该字符在文件中的使用频率作为叶子结点的权,以自底向上的方式,通过n-1次合并运算后构造出一棵树,权值越大的叶子离根越近。

比如我们有一段文字内容为“BADCADFEED”要网络传输给别人,显然用二进制的数字(0和1)来表示是很自然的想法。我们现在这段文字只有六个字母ABCDEF,那么我们可以用相应的二进制数据表示。

这样真正传输的数据就是编码后的“001000011010000011101100100011”,对方接收时可以按照3位一分来译码。如果一篇文章很长,这样的二进制串也将非常的可怕。而且事实上,不管是英文、中文或是其他语言,字母或汉字的出现频率是不相同的,比如英语中的几个元音字母“ae i o u”,中文中的“的 了 有 在”等汉字都是频率极高。

假设六个字母的频率为A 27,B 8,C 15,D15,E 30,F 5,合起来正好是100%。那就意味着,我们完全可以重新按照哈夫曼树来规划它们。下左图为构造哈夫曼树的过程的权值显示。右图为将权值左分支改为0,右分支改为1后的哈夫曼树。

此时,我们对这六个字母用其从树根到叶子所经过路径的0或1来编码,可以得到下表所示这样的定义。

我们将文字内容为“BADCADFEED”再次编码,对比可以看到结果串变小了。

原编码二进制串:001000011010000011101100100011(共30个字符)

新编码二进制串:1001010010101001000111100(共25个字符)

也就是说,我们的数据被压缩了,节约了大约17%的存储或传输成本。随着字符的增加和多字符权重的不同,这种压缩会更加显出其优势。

当我们接收到1001010010101001000111100这样压缩过的新编码时,我们应该如何把它解码出来呢?

编码中非0即1,哈夫曼编码是可变字长编码(VLC)的一种。但是如果长短不等的话其实是很容易混淆的,所以若要设计长短不等的编码,则必须是任一字符的编码都不是另一个字符的编码的前缀,这种编码又称做前缀编码。

按照上表设计的编码就不存在容易与1001、1000混淆的“10”和“100”编码。实际上哈夫曼编码就是一种前缀编码。

但是在解码时,还是要用到哈夫曼树,即发送方和接收方必须要约定好同样的哈夫曼编码规则。

当我们接收到1001010010101001000111100时,由约定好的哈夫曼树可知,1001得到第一个字母是B,接下来01意味着第二个字符是A,如下图所示,其余的也相应的可以得到,从而成功解码。

一般地,设需要编码的字符集为{d1,d2,...,dn},各个字符在电文中出现的次数或频率集合为{w1,w2,...,wn},以d1,d2,...,dn作为叶子结点,以w1,w2,...,wn作为相应叶子结点的权值来构造一棵哈夫曼树。规定哈夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,这就是哈夫曼编码。

作者:刘Java

热心网友 时间:2024-10-04 05:27

哈夫曼树原本是为哈夫曼编码服务的一种数据结构,又称最优二叉树,哈夫曼编码常被使用在数据的压缩和解压缩技术之中。本文详细介绍了哈夫曼树的概念,并且提供了Java实现,最后又介绍了哈夫曼编码。

1 哈夫曼树1.1 哈夫曼树简介

哈夫曼树:给定N个权值作为N个叶子节点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

以下图为例,先说明几个概念:

权:

赋予某个实体的一个量,是对实体的某个或某些属性的数值描述。在数据结构中,实体有节点(元素)和边(关系)两大类,所以对应有节点权和边权。

路径:

在一棵树中,一个结点到另一个结点之间的通路,称为路径。上图,从根结点到结点 h之间的通路就是一条路径。

节点路径长度:

在一棵树中,从一个节点到另一个节点所经过的“边”的数量,被我们称为两个结点之间的路径长度;或者说路径上的分支数目称作路径长度。上图中从根结点到结点 h 的路径长度为3。

树的路径长度:

树的路径长度就是从树根到每一结点的路径长度之和。上图的树的路径长度为1+2+3+3+2+3+1+2+2+3=22

节点的带权路径长度:

树的每一个结点,都可以拥有自己的“权重”(Weight),权重在不同的算法当中可以起到不同的作用。结点的带权路径长度,是指树的根结点到该结点的路径长度,和该结点权重的乘积。上图中h节点带权路径长度为3 * 3=9

树的带权路径长度:

树中所有叶子结点的带权路径长度之和。也被简称为WPL。上图的树的带权路径长度为32+33+34+21+3 * 5=44

1.2 哈夫曼算法

哈夫曼树的构建方法被称为哈夫曼算法,其构建步骤为:

根据给定的n个权值{w1,w2,...,wn}构成n棵二叉树的集合F={T1,T2,...,Tn},其中每棵二叉树Ti中只有一个带权为wi根结点,其左右子树均为空。

在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。

在F中删除这两棵树,同时将新得到的二叉树加入F中。

重复2和3步骤,直到F只含一棵树为止。这棵树便是哈夫曼树。

注意:

叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。

最优二叉树中,权越大的叶子离根越近。

最优二叉树的WPL最小,但是形态不唯一。

1.3 案例

有五个带权节点{ A5,B15,C40,D30,E10}。要求构建哈夫曼树。

先把有权值的叶子结点按照从小到大的顺序排列成一个有序序列,即:A5,E10,B15,D30,C40。

取头两个最小权值的结点作为一个新节点N1的两个子结点,取相对较小的是左孩子(实际上也可以不遵守,因为哈夫曼树是有多种形状的,但是WPL都是最小的),这里就是A为N1的左孩子,E为N1的右孩子,如下图,新结点的权值为两个叶子权值的和5+10=15。

将N1替换A与E,插入有序序列中,保持从小到大排列。即:N115,B15,D30,C40。

重复步骤2。将N1与B作为一个新节点N2的两个子结点。如下图,N2的权值=15+15=30。

将N2替换N1与B,插入有序序列中,保持从小到大排列。即:N230,D30,C40。

重复步骤2。将N2与D作为一个新节点N3的两个子结点。如下图,N3的权值=30+30=60。

将N3替换N2与D,插入有序序列中,保持从小到大排列。即:C40,N360。

重复步骤2。将C与N3作为一个新节点T的两个子结点,如下图,由于T即是根结点,完成哈夫曼树的构造。

此时的上图二叉树的带权路径长度WPL=40×1+30×2+15×3+10×4+5×4=205。这就是最短带权路径长度。

根据最优二叉树形态不唯一的性质和上面的一种形状,我们还可以写出下面形状的最优二叉树:

2 哈夫曼树的实现

现提供哈夫曼树的Java实现,下面的类提供了两种构建方法,区别主要在一个排序使用Arrays.sort排序(注意由于是对象排序,它并非双轴快排),另一个使用了最小堆排序。它们的时间效率是不一致的。 实际使用时选取一种即可。

/***哈夫曼树简单实现*/publicclassHuffmanTree<E>{/***外部保存根节点的引用*/privateBinaryTreeNode<E>root;/***内部节点**@param<E>节点数据类型*/publicstaticclassBinaryTreeNode<E>{//节点数据Edata;//节点权重Stringweight;//左子结点BinaryTreeNode<E>left;//右子节点BinaryTreeNode<E>right;publicBinaryTreeNode(Edata,Stringweight){this.data=data;this.weight=weight;}@OverridepublicStringtoString(){return"Node{"+"data="+data+",weight='"+weight+'\''+'}';}}/***根据指定的普通node节点集合构建哈夫曼树**@parambinaryTreeNodesnode节点集合,采用普通list集合*@param<E>节点数据类型*@return哈夫曼树*/publicstatic<E>HuffmanTree<E>build(List<BinaryTreeNode<E>>binaryTreeNodes){//比较器Comparator<BinaryTreeNode<E>>comparator=(o1,o2)->newBigDecimal(o1.weight).subtract(newBigDecimal(o2.weight)).intValue();while(binaryTreeNodes.size()>1){//手动对集合的节点按照权值大小从大到小进行排序binaryTreeNodes.sort(comparator);//移除并获取权值最小的两个节点BinaryTreeNode<E>left=binaryTreeNodes.remove(0);BinaryTreeNode<E>right=binaryTreeNodes.remove(0);//生成新节点,新节点的权值为两个子节点的权值之和BinaryTreeNode<E>parent=newBinaryTreeNode<>(null,newBigDecimal(left.weight).add(newBigDecimal(right.weight)).toString());//让新节点作为两个权值最小节点的父节点parent.left=left;parent.right=right;//将新节点加入到集合中binaryTreeNodes.add(parent);}//采用循环不断地执行上面的步骤,直到list集合中只剩下一个节点,最后剩下的这个节点就是哈夫曼树的根节点HuffmanTree<E>huffmanTree=newHuffmanTree<>();huffmanTree.root=binaryTreeNodes.remove(0);returnhuffmanTree;}/***根据指定的最小堆构建哈夫曼树**@parambinaryTreeNodesnode节点集合,采用最小堆*@param<E>节点数据类型*@return哈夫曼树*/publicstatic<E>HuffmanTree<E>build(PriorityQueue<BinaryTreeNode<E>>binaryTreeNodes){while(binaryTreeNodes.size()>1){//移除并获取权值最小的两个节点BinaryTreeNode<E>left=binaryTreeNodes.poll();BinaryTreeNode<E>right=binaryTreeNodes.poll();//生成新节点,新节点的权值为两个子节点的权值之和BinaryTreeNode<E>parent=newBinaryTreeNode<>(null,newBigDecimal(left.weight).add(newBigDecimal(right.weight)).toString());//让新节点作为两个权值最小节点的父节点parent.left=left;parent.right=right;//将新节点加入到最小堆中,自动排序binaryTreeNodes.add(parent);}//采用循环不断地执行上面的步骤,直到list集合中只剩下一个节点,最后剩下的这个节点就是哈夫曼树的根节点HuffmanTree<E>huffmanTree=newHuffmanTree<>();huffmanTree.root=binaryTreeNodes.poll();returnhuffmanTree;}/***获取根节点**@return根节点或者null-表示空树*/publicBinaryTreeNode<E>getRoot(){returnroot;}}2.1 测试publicclassHuffmanTreeTest{/*采用用普通集合和最小堆都行,最大的区别是它们的采用不同的排序算法,效率是不一致的*//***采用普通list作为临时存储节点数据的集合,因此我们需要手动排序*/@Testpublicvoidtest1(){//采用普通list作为临时存储节点数据的集合,因此我们需要手动排序List<BinaryTreeNode<String>>binaryTreeNodes=newArrayList<>();//A5,B15,C40,D30,E10binaryTreeNodes.add(newBinaryTreeNode<>("A","5"));binaryTreeNodes.add(newBinaryTreeNode<>("B","15"));binaryTreeNodes.add(newBinaryTreeNode<>("C","40"));binaryTreeNodes.add(newBinaryTreeNode<>("D","30"));binaryTreeNodes.add(newBinaryTreeNode<>("E","10"));HuffmanTree<String>huffmanTree=HuffmanTree.build(binaryTreeNodes);BinaryTreeNode<String>root=huffmanTree.getRoot();System.out.println(root);}/***采用最小堆--priorityQueue作为临时存储节点数据的集合,priorityQueue的性质就是对集合的元素进行自动排序,因此我们不必手动排序*/@Testpublicvoidtest2(){//采用最小堆--priorityQueue.作为临时存储节点数据的集合,priorityQueue的性质就是对集合的元素进行自动排序,我们只需要指定排序规则PriorityQueue<BinaryTreeNode<String>>priorityQueueBinaryTreeNodes=newPriorityQueue<>((o1,o2)->newBigDecimal(o1.weight).subtract(newBigDecimal(o2.weight)).intValue());priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("A","5"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("B","15"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("C","40"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("D","30"));priorityQueueBinaryTreeNodes.add(newBinaryTreeNode<>("E","10"));HuffmanTree<String>huffmanTree=HuffmanTree.build(priorityQueueBinaryTreeNodes);BinaryTreeNode<String>root=huffmanTree.getRoot();System.out.println(root);}}3 哈夫曼编码

哈夫曼树的出现主要是为了哈夫曼编码服务的,哈夫曼编码有着非常广泛的应用,哈夫曼编码常被使用在数据的压缩和解压缩技术之中。

哈夫曼编码的基本思想是以字符的使用频率作为权,构造一棵哈夫曼树,然后利用哈夫曼树对字符进行编码。这棵哈夫曼树,是将所要编码的字符作为叶子结点,该字符在文件中的使用频率作为叶子结点的权,以自底向上的方式,通过n-1次合并运算后构造出一棵树,权值越大的叶子离根越近。

比如我们有一段文字内容为“BADCADFEED”要网络传输给别人,显然用二进制的数字(0和1)来表示是很自然的想法。我们现在这段文字只有六个字母ABCDEF,那么我们可以用相应的二进制数据表示。

这样真正传输的数据就是编码后的“001000011010000011101100100011”,对方接收时可以按照3位一分来译码。如果一篇文章很长,这样的二进制串也将非常的可怕。而且事实上,不管是英文、中文或是其他语言,字母或汉字的出现频率是不相同的,比如英语中的几个元音字母“ae i o u”,中文中的“的 了 有 在”等汉字都是频率极高。

假设六个字母的频率为A 27,B 8,C 15,D15,E 30,F 5,合起来正好是100%。那就意味着,我们完全可以重新按照哈夫曼树来规划它们。下左图为构造哈夫曼树的过程的权值显示。右图为将权值左分支改为0,右分支改为1后的哈夫曼树。

此时,我们对这六个字母用其从树根到叶子所经过路径的0或1来编码,可以得到下表所示这样的定义。

我们将文字内容为“BADCADFEED”再次编码,对比可以看到结果串变小了。

原编码二进制串:001000011010000011101100100011(共30个字符)

新编码二进制串:1001010010101001000111100(共25个字符)

也就是说,我们的数据被压缩了,节约了大约17%的存储或传输成本。随着字符的增加和多字符权重的不同,这种压缩会更加显出其优势。

当我们接收到1001010010101001000111100这样压缩过的新编码时,我们应该如何把它解码出来呢?

编码中非0即1,哈夫曼编码是可变字长编码(VLC)的一种。但是如果长短不等的话其实是很容易混淆的,所以若要设计长短不等的编码,则必须是任一字符的编码都不是另一个字符的编码的前缀,这种编码又称做前缀编码。

按照上表设计的编码就不存在容易与1001、1000混淆的“10”和“100”编码。实际上哈夫曼编码就是一种前缀编码。

但是在解码时,还是要用到哈夫曼树,即发送方和接收方必须要约定好同样的哈夫曼编码规则。

当我们接收到1001010010101001000111100时,由约定好的哈夫曼树可知,1001得到第一个字母是B,接下来01意味着第二个字符是A,如下图所示,其余的也相应的可以得到,从而成功解码。

一般地,设需要编码的字符集为{d1,d2,...,dn},各个字符在电文中出现的次数或频率集合为{w1,w2,...,wn},以d1,d2,...,dn作为叶子结点,以w1,w2,...,wn作为相应叶子结点的权值来构造一棵哈夫曼树。规定哈夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,这就是哈夫曼编码。

作者:刘Java

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
水利水电工程单元工程施工质量验收评定标准——土石方工程(SL 631... 关于水利水电工程项目划分的规程有哪些?划分的越细越好,感谢大家的帮 ... 水利水电工程的单位工程包括哪些工程? 尖子生学案:生物高中选修3目录 U盘传了数据怎么删除电脑记录如何清除电脑上U盘使用记录 把u盘插电脑怎么删掉记忆如何删掉电脑上插过U盘的记录 win7清除u盘记录 如何清理win7电脑u盘痕迹如何清除电脑上U盘使用记录 麦当劳和华莱士的外卖电话是什么? 一文详解ISO/IEC20000信息技术服务管理体系认证好处、材料、流程 求解,在寝室里用烤火器会不会跳闸 单片机 79=( )H=( )B 10010101B=( )十进制=( )H 摔断腰骨吃什么恢复快 腰间盘睡觉什么姿势好 大艺电动工具和东成电动工具哪一个好? 姓名,保密。性别,男。年龄,貌似比我大。外貌,一般。身高,目测比我矮... 梦到了一个奇怪的梦 梦到被男人追赶求解 王鹤棣为什么可以这么火 从道明寺到东方青苍他变化多大 带各种枪的恐怖单机游戏? 电脑无法截图能用浏览器或QQ快速解决吗? 捷克胡司战争捷克简介 波西米亚哪个国家 如何快速隐藏和显示Photoshop的工具栏和浮动面板? 如何通过PHOTOSHOP图层技巧进行图像处理? ps按shift画直线会连在一起 ps怎么两点连接一条直线ps中怎样两点连直线 学习Photoshop必要的十条小技巧 镇江宋元古街悠久历史 西津古渡简介 华为Mate 9保时捷限量版配置升级是否物超所值? 高中,人教版与新课标的差距大吗,对复习生又有多大影响 哈夫曼哈夫曼简介 母狗来月经了能交配吗? 狗狗月经时交配会怀孕吗? 喝完茶后茶具怎么清洗 茶具清洁剂茶垢危害 怎样轻松除茶垢? 茶盘上的茶渍要用什么洗才能洗掉 ...别跟我提起百度 求图文并茂 谢谢 各位大神鼎力相助 word怎么把空白栏去掉怎么弄掉word里面空白框 如何去掉word表格中蓝色框线 苹果怎么停止家庭共享啊!? 金山文档停止共享编辑是什么意思啊? 大家知道这种图文图片是怎么制作的吗?除了用PS还有什么办法 ...可以用手机制作吗。或者有没有制作这种图的手机app(应用)。_百度... 求手机图片处理的软件(如图) 就是可以制作这种文字里面有文字的 不要... 有社保有流水有工作证明可以办理贷款买车吗 这种图片怎样用PS制作,就是有弯曲的效果 ...已经满足条件,但半年前离职一直到现在,请问还能买车吗?