贪心算法_哈夫曼编码问题(Huffman Coding)

原文链接:  http://blog.csdn.net/u011638883/article/details/16857309

      哈夫曼编码问题,可以用贪心算法来解。下面来详细讨论一下哈夫曼编码问题。

      问题描述:现有一个文本文件,其中包含的字符数据出现的次数各不相同,先要求对该文本中包含的字符进行编码,使文本占用的位数更小。

      问题分析:我们知道文件的存储都是以二进制数表示的,如:字符c可以表示为010101...之类的。因为不同的操作系统对于不同的数据类型会分配给相同的数据容器长度,如java中int型数据固定占用4个字节的存储空间。现在问题时因为各个字符出现的概率不同,那么我们就可以给出现概率高的字符分配以"短"的二进制表示数,给出现概率低的字符分配以"长"的二进制表示数。从而达到降低平均每字符占用的空间数,进而实现无损的空间压缩。

      OK,我们来论证哈夫曼编码问题的贪心选择性质。这里必须介绍一下的是,我们会使用二叉树这种数据结构来解哈夫曼问题。从根节点到叶节点经过的路径就是某个叶节点对象(这里就是字符)的编码值。那么从直觉(恩,直觉,我觉的解贪心算法的话,直觉很重要)上将讲,应该将概率低的元素放置到树的底部,将概率高的元素放置到树的顶部。代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.wly.algorithmbase.greedy;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. /** 
  6.  * 贪心算法解哈夫曼编码问题 
  7.  *  
  8.  * @author wly 
  9.  *  
  10.  */  
  11. public class HuffmanCode {  
  12.   
  13.     public static void main(String[] args) {  
  14.   
  15.         ArrayList<HuffmanNode> list = new ArrayList<HuffmanNode>();  
  16.   
  17.         list.add(new HuffmanNode(nullnull"A"0.3f));  
  18.         list.add(new HuffmanNode(nullnull"B"0.1f));  
  19.         list.add(new HuffmanNode(nullnull"C"0.35f));  
  20.         list.add(new HuffmanNode(nullnull"D"0.05f));  
  21.         list.add(new HuffmanNode(nullnull"E"0.2f));  
  22.   
  23.         print(getHuffmanCodeNode(list));  
  24.     }  
  25.   
  26.     /** 
  27.      * 得到表示当前输入节点的树结构 
  28.      * @param list 
  29.      * @return 
  30.      */  
  31.     public static HuffmanNode getHuffmanCodeNode(ArrayList<HuffmanNode> list) {  
  32.   
  33.         while (list.size() >= 2) {  
  34.             //1.排序元素  
  35.             srotNodeListByKey(list);  
  36.   
  37.             //2.合并key值最小的两个节点(因为已经排序过了,此处就是列表的前两项)  
  38.             HuffmanNode newNode = combine2SmallestNode(list.get(0), list.get(1));  
  39.             list.remove(0);  
  40.             list.remove(0); //注意ArrayList中remove元素时的索引移动s  
  41.             list.add(0, newNode);  
  42.         }  
  43.           
  44.         return list.get(0);  
  45.     }  
  46.       
  47.     /** 
  48.      * 打印某个节点的树结构,即以该节点为根节点的子树结构s 
  49.      * @param node 
  50.      */  
  51.     public static void print(HuffmanNode node) {  
  52.         System.out.print("| " + node.getData() + "," + node.getPercent() + " |");  
  53.         if(node.getLeftN() != null) {  
  54.             print(node.getLeftN());  
  55.         }   
  56.           
  57.         if(node.getRightN() != null) {  
  58.             print(node.getRightN());  
  59.         }   
  60.     }     
  61.       
  62.     /** 
  63.      * 使用冒泡排序,按key值单调递增排序 
  64.      *  
  65.      * @param list 
  66.      */  
  67.     public static void srotNodeListByKey(ArrayList<HuffmanNode> list) {  
  68.         for (int i = 0; i < list.size(); i++) {  
  69.             for (int j = i+1; j < list.size(); j++) {  
  70.                 if (list.get(i).getPercent() > list.get(j).getPercent()) {  
  71.                     // 交换位置  
  72.                     list.add(i, list.get(j));  
  73.                     list.remove(j+1);  
  74.                       
  75.                     list.add(j, list.get(i + 1));  
  76.                     list.remove(i + 1);  
  77.                 }  
  78.             }  
  79.         }  
  80.     }  
  81.   
  82.   
  83.     /** 
  84.      * 将两个子节点合成为一个父节点 
  85.      *  
  86.      * @param leftNode 
  87.      * @param rightNode 
  88.      * @return 
  89.      */  
  90.     private static HuffmanNode combine2SmallestNode(HuffmanNode leftNode,  
  91.             HuffmanNode rightNode) {  
  92.         HuffmanNode parentNode = new HuffmanNode(leftNode, rightNode,  
  93.                 leftNode.getData() + rightNode.getData(), leftNode.getPercent()  
  94.                         + rightNode.getPercent());  
  95.         return parentNode;  
  96.     }  
  97. }  
  98.   
  99. /** 
  100.  * 用于表示哈夫曼编码的二叉树的节类 
  101.  *  
  102.  * @author wly 
  103.  *  
  104.  */  
  105. class HuffmanNode {  
  106.   
  107.     private HuffmanNode leftN; //左子节点  
  108.     private HuffmanNode rightN; //右子节点  
  109.     private String data; // 包含的数据,本程序中指的是字符  
  110.     private float percent; // 检索key值  
  111.   
  112.     public HuffmanNode(HuffmanNode leftN, HuffmanNode rightN, String data,  
  113.             float key) {  
  114.         super();  
  115.         this.leftN = leftN;  
  116.         this.rightN = rightN;  
  117.         this.data = data;  
  118.         this.percent = key;  
  119.     }  
  120.   
  121.     public float getPercent() {  
  122.         return percent;  
  123.     }  
  124.   
  125.     public void setPercent(float percent) {  
  126.         this.percent = percent;  
  127.     }  
  128.   
  129.     public HuffmanNode getLeftN() {  
  130.         return leftN;  
  131.     }  
  132.   
  133.     public void setLeftN(HuffmanNode leftN) {  
  134.         this.leftN = leftN;  
  135.     }  
  136.   
  137.     public HuffmanNode getRightN() {  
  138.         return rightN;  
  139.     }  
  140.   
  141.     public void setRightN(HuffmanNode rightN) {  
  142.         this.rightN = rightN;  
  143.     }  
  144.   
  145.     public String getData() {  
  146.         return data;  
  147.     }  
  148.   
  149.     public void setData(String data) {  
  150.         this.data = data;  
  151.     }  
  152.   
  153. }  
       运行结果:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. | DBEAC,1.0 || DBE,0.35000002 || DB,0.15 || D,0.05 || B,0.1 || E,0.2 || AC,0.65 || A,0.3 || C,0.35 |  

      从运行结果可以得到二叉树如下:

           即各个字符的编码分别是:D:000、D:001、E:01、A:10、C:11

      若不进行编码按相同字长编码,则至少需要3位,那么需要存储ABCDE的尺寸为:3*1=3

      编码后,存储ABCDE的尺寸为:3*0.05+3*0.1+2*0.2+2*0.3+2*0.2=1.85

      可见使用哈夫曼编码能够在一定程度上实现数据的无损压缩。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值