哈夫曼(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
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
希望和大家多多交流!
分类: 顽童读书---数据结构与算法