Huffman树、Huffman编码及代码实现

基本概念

结点的路径
从根结点到该结点所经历的结点和分支序列。

结点的路径长度
从根结点到该结点的路径上分支的数目。

结点的权
结点被人为赋予的一个具有实际意义的数值,如重要程度、成本等。

结点的带权路径长度
结点的路径长度与该结点的权值的乘积。

树的带权路径长度
树中所有叶子结点的带权路径长度之和。
假设树有 n n n个叶子节点,该树的带权路径长度通常记作: W P L = ∑ i = 1 n W i × L i WPL=\sum_{i=1}^{n}{W_i \times L_i} WPL=i=1nWi×Li其中 W i W_i Wi为带权 L i L_i Li的叶子结点的路径长度。

最优二叉树
给定 n n n个权值的 n n n个叶子结点,按照一定的规则构造一棵二叉树,使树的带权路径长度达到最小值,则这棵树被程为最优二叉树,也称哈夫曼树(Huffman Tree)。

哈夫曼树的构造方法

  1. 根据给定的 n n n个权值,构造一个由 n n n棵二叉树所构成的集合(森林)。 F = T 1 , T 2 , . . . , T n , F={T_1, T_2, ..., T_n}, F=T1,T2,...,Tn,其中每棵二叉树均只带一个权值为 w i w_i wi的根结点,其左、右子树为空;
  2. 在二叉树森林 F F F中选取根结点的权值最小和次小的两棵二叉树 T i T_i Ti T j T_j Tj,分别把它们作为左、右子树构造一棵新二叉树,新二叉树的根结点权值为 T i T_i Ti T j T_j Tj的根结点的权值之和;
  3. T i T_i Ti T j T_j Tj从森林 F F F中删除,同时加入新产生的二叉树;
  4. 重复2,3两步,直至 F F F中只有一棵二叉树为止,则这棵二叉树即为所构成的哈夫曼树。

例如:
给定权值 W = { 2 , 4 , 8 , 9 , 5 } W=\{2,4,8,9,5\} W={2,4,8,9,5}, 构成森林 F F F:
在这里插入图片描述
选取根结点最小和次小的两棵树,根结点分别为2,4,构成新二叉树,其根结点权值为左、右子树根结点的权值之和:
在这里插入图片描述
用生成的新树替换 F F F中它的左右子树:
在这里插入图片描述
再找出新生成的 F F F中根结点权值最小和次小的两棵树,重复以上步骤:
在这里插入图片描述
在这里插入图片描述
最终得到一棵二叉树,即为生成的哈夫曼树:
在这里插入图片描述

哈夫曼编码

用哈夫曼树编码

步骤如下:

  1. 将需要编码的每个字符用一个结点代表,按照具体的用途赋予相应的权值,构成结点集 F F F
  2. F F F中的所有结点构成一棵哈夫曼树。
  3. 对于每一个字符,其编码为:从根结点到该结点的路径,若为左分支,则该位为0,若为右分支,则该位为1。编码的长度即为结点的路径长度。

如对于以下哈夫曼树:
在这里插入图片描述
字符“B” 的结点的路径如蓝线所示,从根结点向下路径分支依次为"左-右-左", 因此其哈夫曼编码为“010”。同理,字符“A”的编码为“00”, 字符"C"的编码为"011",字符“D",“E"的编码分别为"10”,“11"。

如果各个字符的权值代表其出现频率,按照哈夫曼编码,权值越大的结点越晚被合并,出现频率越大的字符哈夫曼编码的长度越小,这将能够用于文本压缩。

哈夫曼编码是一种前缀编码。前缀编码指的是,任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀。由哈夫曼编码的过程可以知道,编码即是代表这一字符所代表的结点在哈夫曼树中的路径。因为每一个字符的结点均为叶子结点,因此并不会有经由此结点到达的另一结点,因此不存在以此字符的编码为前缀的另一编码。

用哈夫曼树译码

译码过程是编码过程的逆过程。

译码方法为:从哈夫曼树的根开始,从左到右把二进制编码的每一位进行判别,若二进制编码为0,则选择左分支走向下一个结点;若遇到1,则选择右分支走向下一个结点。

代码实现

使用下表中的字符集(包含26个大写英文字母及字母频率权值)构造一棵huffman树,并根据此huffman树得到该字符集中每个字母的huffman编码。

字符Value字符Value字符Value字符Value字符Value字符Value字符Value
A7B2C2D3E11F2G2
H6I6J1K1L4M3N7
O9P2Q1R6S6T8U4
V1W2X1Y2Z1

这里使用了优先队和二叉树进行实现。调用的包是自己写的数据结构,换成java内置的库即可。

package dataStructure;
import dataStructure.linearList.PriorityQData;
import dataStructure.linearList.PriorityQueue;
import dataStructure.tree.BiTree;
import dataStructure.tree.BiTreeNode;
import dataStructure.linearList.Node;

public class ApplicationHuffmanTree {
       public static void main(String[] args) throws Exception {  
              int[] wList = {7,2,2,3,11,2,2,6,6,1,1,4,3,7,9,2,1,6,6,8,4,1,2,1,2,1};
              char[] cList = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','V','U','W','X','Y','Z'};
              HuffmanTree hfmT = new HuffmanTree(cList,wList);              
              hfmT.getHuffmanCode(hfmT.getRoot(),"");
              System.out.println();
       }
}

class HuffmanTree extends BiTree {
       public HuffmanTree(char[] cList, int[] wList) throws Exception {
              if(cList.length != wList.length)
                     throw new Exception("权值序列与字母序列不匹配");
              PriorityQueue que = new PriorityQueue();       
              for(int i = 0; i < cList.length; i++){
                     BiTreeNode node = new BiTreeNode(cList[i]);
                     PriorityQData<BiTreeNode> x = new PriorityQData(node,wList[i]);
                     que.offer(x);   
              }
              for(int j = 0; j < cList.length-1; j++){
                     PriorityQData<BiTreeNode> l = que.poll(); 
                     PriorityQData<BiTreeNode> r = que.poll();
                     int p = l.getPriority()+r.getPriority();
                     BiTreeNode newNode = new BiTreeNode();
                     newNode.setLchild(l.getElem());
                     newNode.setRchild(r.getElem());
                     PriorityQData<BiTreeNode> y = new PriorityQData(newNode,p);
                     que.offer(y);   
                     this.root = newNode;                 
              }
       }       

       //利用深度优先遍历求出huffman树的huffman编码
       public void getHuffmanCode(BiTreeNode T, String code) {
            if (T.getLchild() == null){       
       		System.out.println(T.getData() + ": " + code);
            }
            else{       
       		getHuffmanCode(T.getLchild(),code.concat("0"));       
       		getHuffmanCode(T.getRchild(),code.concat("1"));
            }
       }
}

运行效果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值