哈夫曼(Huffman)编码

  在学习二叉树时看到关于哈夫曼编码的一些描述,兴趣来潮,自己写一个算法。哈夫曼算法使用二叉树

以令人惊讶的方式来压缩数据,以提高数据传输的效率和时间。只有知道哈夫曼编码而不会写代码的童鞋们

才会在网上搜代码,故在这里对哈夫曼编码不做过多介绍。

  实现哈弗曼(Huffman)算法的编码(Encode)与解码(Encode).

  分为以下四步来完成这项编码

  1.Create a Huffman tree for this message.
    2.Create a code table.
    3.Encode the message into binary.
    4.Decode the message from binary back to
      text.

  以下这段代码逻辑正确,测试结果也正确,但仍有一些缺陷还没解决。比如编码时出现频率最多的字符编码位数要最少,

这样得到的编码位数少效率高,这段代码并没做到。其次还有对优先级队列运用不是很准确,不能精准的控制它.remove出的

元素有时不符合我的预期。若有有心人的话,可在这个基础上二次开发,并将更完善的代码发我一份,共同学习。

  本程序是用Java编写的。 

  一.Node.java

 1 package main;
 2 
 3 class Node 
 4 {
 5     char cData;
 6     int iNumber;
 7     Node leftChild;
 8     Node rightChild;
 9     
10     public void displayNode()
11     {
12         System.out.print("Node:");
13         System.out.print("{");
14         System.out.print(cData);
15         System.out.print(",");
16         System.out.print(iNumber);
17         System.out.print("}");
18     }
19 }

   二.HuffmanTree.java

 1 package main;
 2 
 3 class HuffmanTree 
 4 {
 5     private Node root;
 6     
 7     public HuffmanTree(Node root)
 8     {
 9         this.root = root;
10     }
11     
12     //获取root的引用,以便用来组装新树
13     public Node getRoot()
14     {
15         return this.root;
16     }
17     
18     //获取iNumber的值,代表频率数
19     public int getNumber()
20     {
21         if(root != null)
22             return root.iNumber;
23         else
24             return 0;
25     }
26 }

  三.Main.java

  1 /******************************************************
  2 Copyright:bupt
  3 Author:zhai bao hua
  4 Begin:2013-10-12
  5 End:2013-10-17
  6 E-mail:carman_loneliness@163.com
  7 Description:实现哈弗曼(Huffman)算法的编码(Encode)与
  8             解码(Encode).哈弗曼编码是一种数据压缩算
  9             法,以节省数据传输的时间,提高效率。
 10             分为以下四步来完成这项编码
 11             1.Create a Huffman tree for this message.
 12             2.Create a code table.
 13             3.Encode the message into binary.
 14             4.Decode the message from binary back to
 15               text.
 16 Description2:这段代码逻辑正确,测试结果也正确,但仍有一些
 17                缺陷还没解决。比如编码时出现频率最多的字符编码
 18                位数要最少,这样得到的编码位数少效率高,这段代码
 19                并没做到。其次还有对优先级队列运用不是很准确,
 20                不能精准的控制它.remove出的元素有时不符合我的
 21                预期。若有有心人的话,可在这个基础上二次开发,
 22                并将更完善的代码发我一份,共同学习。
 23 *******************************************************/
 24 package main;
 25 
 26 import java.util.Comparator;
 27 import java.util.HashMap;
 28 import java.util.PriorityQueue;
 29 import java.util.Set;
 30 
 31 public class Main
 32 {
 33     static HashMap<String,Integer> hmNumberTable;    //频率表
 34     static PriorityQueue<HuffmanTree> pqList;    //所有树的队列
 35     static HuffmanTree hTree;    //表示哈夫曼树
 36     static HashMap<String, String> hmCodeTable;    //代码表
 37     static String sHuffman = "";    //Encode的字符串
 38     static String sDecode = "";    //Decode的字符串
 39     
 40     public static void main(String[] args) 
 41     {
 42         //test word
 43         String sSample = "TODAY IS A GOOD DAY";
 44         
 45         /*一.Create a Huffman tree for this message
 46          */
 47         
 48         //得到每个字符出现几率频率表
 49         hmNumberTable = gethmNumberTable(sSample);
 50         
 51         //定义优先级队列,key值小的排在先
 52         Comparator<HuffmanTree> OrderIsdn =  new Comparator<HuffmanTree>() 
 53         {
 54             @Override
 55             public int compare(HuffmanTree o1, HuffmanTree o2) 
 56             {
 57                 int numbera = o1.getNumber();  
 58                 int numberb = o2.getNumber();  
 59                 if(numberb > numbera)  
 60                 {
 61                     return -1;  
 62                 }  
 63                 else if(numberb < numbera)  
 64                 {
 65                     return 1;  
 66                 }  
 67                 else  
 68                 {
 69                     return 0;  
 70                 } 
 71             }
 72         };
 73         pqList = new PriorityQueue<HuffmanTree>(hmNumberTable.size(),OrderIsdn);
 74         
 75         /*操作步骤
 76          *1.为消息中的每个字符创建一个Node对象,每个节点有两个数据项:
 77          *字符和字符在消息中出现的频率。
 78          *2.为这些节点创建tree对象
 79          *3.把这些树都插入到优先级队列pqList当中去,它们按频率排序,
 80          *频率小的有最高优先级。
 81          */
 82         Set<String> sTemp = hmNumberTable.keySet();
 83         for(String string:sTemp)
 84         {
 85             Node node = new Node();
 86             node.cData = string.charAt(0);
 87             node.iNumber = hmNumberTable.get(string);
 88             HuffmanTree hTempTree = new HuffmanTree(node);
 89             pqList.add(hTempTree);
 90         }
 91         
 92         /*操作步骤
 93          *1.从pqList中删除两棵树,并把它们作为一个新节点的子节点。
 94          *新节点的频率是子节点频率的和,它的字符字段可以是空的。
 95          *2.把这个新的三节点树插回优先级队列里。
 96          *3.反复重复第一步和第二步。树会越变越大,队列中的数据项会
 97          *越来越少。当队中只有一颗树时,它就是所建的哈夫曼树了。
 98          */
 99         while(pqList.size() > 1)
100         {
101             HuffmanTree hTempA = pqList.peek();
102             pqList.remove();
103             HuffmanTree hTempB = pqList.peek();
104             pqList.remove();
105             Node node = new Node();
106             node.cData = '$';    //$作为一个特别的char,用作识别。
107             node.iNumber = hTempA.getNumber() + hTempB.getNumber();
108             node.leftChild = hTempA.getRoot();
109             node.rightChild = hTempB.getRoot();
110             HuffmanTree hTempC = new HuffmanTree(node);
111             pqList.add(hTempC);
112             //测试单元,遍历队列
113 //            traveQueue(pqList);
114         }
115         hTree = pqList.peek();
116         
117         /*二.Create a code table.
118          *得到hmCodeTable
119          */
120         
121         hmCodeTable = new HashMap<String, String>();
122         getPaths(hTree.getRoot(),"");
123         
124         /*三.Encode the message into binary.
125          */
126         for(char cTemp:sSample.toCharArray())
127         {
128             String string = hmCodeTable.get(String.valueOf(cTemp));
129             sHuffman = sHuffman + string;
130         }
131         System.out.println("Huffman Code:");
132         System.out.println(sHuffman);
133         
134         /*四.Decode the message from binary back to
135          *     text.
136          */
137         int index = 0;
138         char cIndex;
139         Node nIndexNode = hTree.getRoot();
140         while(index < sHuffman.length())
141         {
142             cIndex = sHuffman.charAt(index);
143             if(cIndex == '1')
144             {
145                 nIndexNode = nIndexNode.rightChild;
146             }
147             else if(cIndex == '0')
148             {
149                 nIndexNode = nIndexNode.leftChild;
150             }
151             if(nIndexNode.leftChild == null && nIndexNode.rightChild == null)
152             {
153                 sDecode = sDecode + nIndexNode.cData;
154                 nIndexNode = hTree.getRoot();
155             }
156             index ++;
157         }
158         System.out.println("Decode:");
159         System.out.println(sDecode);
160     }
161     
162     //得到频率的Hash表
163     private static HashMap<String,Integer> gethmNumberTable(String sSample) 
164     {
165         HashMap<String,Integer> hmList = new HashMap<String,Integer>();
166         for(int i = 0;i < sSample.length();i++)
167         {
168             char temp = sSample.charAt(i);
169             String sTemp = String.valueOf(temp);
170             if(hmList.containsKey(sTemp))
171             {
172                 int value = hmList.get(sTemp) + 1;
173                 hmList.remove(sTemp);
174                 hmList.put(sTemp, value);
175             }
176             else
177             {
178                 hmList.put(sTemp,1);
179             }
180         }
181         return hmList;
182     }
183     
184     /*递归遍历所有节点,并保存所有叶子节点的路径
185      *node:父节点
186      *myPath:父节点本身的路径
187      *向左走一步为0,向右走一步为1.
188      *终止条件:遍历到叶子节点为止.因此可遍历完所有叶子节点
189      *递归代码很简单,但理清思路确花了我好久的时间。
190      */
191     private static void getPaths(Node node,String myPath)
192     {
193         String[] sPaths = new String[2];
194         if(node.leftChild == null && node.rightChild == null)
195         {
196             System.out.println("{" + node.cData + ":" + myPath + "}");
197             hmCodeTable.put(String.valueOf(node.cData), myPath);
198             return;
199         }
200         if(node.leftChild != null)
201         {
202             sPaths[0] = myPath + "0";
203             getPaths(node.leftChild,sPaths[0]);
204         }
205         if(node.rightChild != null)
206         {
207             sPaths[1] = myPath + "1";
208             getPaths(node.rightChild,sPaths[1]);
209         }
210         return;
211     }
212     
213     /*UNIT*UNIT
214      *测试代码 
215      *遍历队列但不删除元素
216      */
217     private static void traveQueue(PriorityQueue<HuffmanTree> list)
218     {
219         HuffmanTree[] trees = new HuffmanTree[list.size()];
220         int i = 0;
221         while(list.size() > 0)
222         {
223             HuffmanTree tree = list.peek();
224             System.out.print(tree.getRoot().cData + ":" + tree.getNumber() + " ");
225             list.remove();
226             trees[i] = tree;
227             i++;
228         }
229         System.out.println();
230         for(HuffmanTree theTree:trees)
231         {
232             list.add(theTree);
233         }
234     }
235 }

  邮箱:carman_loneliness@163.com

  希望和大家多多交流!

posted @ 2013-10-17 14:39  Andy Zhai  阅读(2046)  评论(0编辑  收藏  举报