哈夫曼(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:[email protected]

  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 }

  邮箱:[email protected]

  希望和大家多多交流!

你可能感兴趣的:(Huffman)