浅谈哈夫曼树和哈夫曼编码
在学习二叉树时看到关于哈夫曼编码的一些描述,兴趣来潮,自己写一个算法。哈夫曼算法使用二叉树
以令人惊讶的方式来压缩数据,以提高数据传输的效率和时间。只有知道哈夫曼编码而不会写代码的童鞋们
才会在网上搜代码,故在这里对哈夫曼编码不做过多介绍。
实现哈弗曼(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
package main; class Node { char cData; int iNumber; Node leftChild; Node rightChild; public void displayNode() { System.out.print("Node:"); System.out.print("{"); System.out.print(cData); System.out.print(","); System.out.print(iNumber); System.out.print("}"); } }
二、HuffmanTree.java
package main; class HuffmanTree { private Node root; public HuffmanTree(Node root) { this.root = root; } //获取root的引用,以便用来组装新树 public Node getRoot() { return this.root; } //获取iNumber的值,代表频率数 public int getNumber() { if(root != null) return root.iNumber; else return 0; } }
三、Main.java
/****************************************************** Copyright:bupt Author:zhai bao hua Begin:2013-10-12 End:2013-10-17 E-mail:carman_loneliness@163.com Description:实现哈弗曼(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. Description2:这段代码逻辑正确,测试结果也正确,但仍有一些 缺陷还没解决。比如编码时出现频率最多的字符编码 位数要最少,这样得到的编码位数少效率高,这段代码 并没做到。其次还有对优先级队列运用不是很准确, 不能精准的控制它.remove出的元素有时不符合我的 预期。若有有心人的话,可在这个基础上二次开发, 并将更完善的代码发我一份,共同学习。 *******************************************************/ package main; import java.util.Comparator; import java.util.HashMap; import java.util.PriorityQueue; import java.util.Set; public class Main { static HashMap<String,Integer> hmNumberTable; //频率表 static PriorityQueue<HuffmanTree> pqList; //所有树的队列 static HuffmanTree hTree; //表示哈夫曼树 static HashMap<String, String> hmCodeTable; //代码表 static String sHuffman = ""; //Encode的字符串 static String sDecode = ""; //Decode的字符串 public static void main(String[] args) { //test word String sSample = "TODAY IS A GOOD DAY"; /*一.Create a Huffman tree for this message */ //得到每个字符出现几率频率表 hmNumberTable = gethmNumberTable(sSample); //定义优先级队列,key值小的排在先 Comparator<HuffmanTree> OrderIsdn = new Comparator<HuffmanTree>() { @Override public int compare(HuffmanTree o1, HuffmanTree o2) { int numbera = o1.getNumber(); int numberb = o2.getNumber(); if(numberb > numbera) { return -1; } else if(numberb < numbera) { return 1; } else { return 0; } } }; pqList = new PriorityQueue<HuffmanTree>(hmNumberTable.size(),OrderIsdn); /*操作步骤 *1.为消息中的每个字符创建一个Node对象,每个节点有两个数据项: *字符和字符在消息中出现的频率。 *2.为这些节点创建tree对象 *3.把这些树都插入到优先级队列pqList当中去,它们按频率排序, *频率小的有最高优先级。 */ Set<String> sTemp = hmNumberTable.keySet(); for(String string:sTemp) { Node node = new Node(); node.cData = string.charAt(0); node.iNumber = hmNumberTable.get(string); HuffmanTree hTempTree = new HuffmanTree(node); pqList.add(hTempTree); } /*操作步骤 *1.从pqList中删除两棵树,并把它们作为一个新节点的子节点。 *新节点的频率是子节点频率的和,它的字符字段可以是空的。 *2.把这个新的三节点树插回优先级队列里。 *3.反复重复第一步和第二步。树会越变越大,队列中的数据项会 *越来越少。当队中只有一颗树时,它就是所建的哈夫曼树了。 */ while(pqList.size() > 1) { HuffmanTree hTempA = pqList.peek(); pqList.remove(); HuffmanTree hTempB = pqList.peek(); pqList.remove(); Node node = new Node(); node.cData = '$'; //$作为一个特别的char,用作识别。 node.iNumber = hTempA.getNumber() + hTempB.getNumber(); node.leftChild = hTempA.getRoot(); node.rightChild = hTempB.getRoot(); HuffmanTree hTempC = new HuffmanTree(node); pqList.add(hTempC); //测试单元,遍历队列 // traveQueue(pqList); } hTree = pqList.peek(); /*二.Create a code table. *得到hmCodeTable */ hmCodeTable = new HashMap<String, String>(); getPaths(hTree.getRoot(),""); /*三.Encode the message into binary. */ for(char cTemp:sSample.toCharArray()) { String string = hmCodeTable.get(String.valueOf(cTemp)); sHuffman = sHuffman + string; } System.out.println("Huffman Code:"); System.out.println(sHuffman); /*四.Decode the message from binary back to * text. */ int index = 0; char cIndex; Node nIndexNode = hTree.getRoot(); while(index < sHuffman.length()) { cIndex = sHuffman.charAt(index); if(cIndex == '1') { nIndexNode = nIndexNode.rightChild; } else if(cIndex == '0') { nIndexNode = nIndexNode.leftChild; } if(nIndexNode.leftChild == null && nIndexNode.rightChild == null) { sDecode = sDecode + nIndexNode.cData; nIndexNode = hTree.getRoot(); } index ++; } System.out.println("Decode:"); System.out.println(sDecode); } //得到频率的Hash表 private static HashMap<String,Integer> gethmNumberTable(String sSample) { HashMap<String,Integer> hmList = new HashMap<String,Integer>(); for(int i = 0;i < sSample.length();i++) { char temp = sSample.charAt(i); String sTemp = String.valueOf(temp); if(hmList.containsKey(sTemp)) { int value = hmList.get(sTemp) + 1; hmList.remove(sTemp); hmList.put(sTemp, value); } else { hmList.put(sTemp,1); } } return hmList; } /*递归遍历所有节点,并保存所有叶子节点的路径 *node:父节点 *myPath:父节点本身的路径 *向左走一步为0,向右走一步为1. *终止条件:遍历到叶子节点为止.因此可遍历完所有叶子节点 *递归代码很简单,但理清思路确花了我好久的时间。 */ private static void getPaths(Node node,String myPath) { String[] sPaths = new String[2]; if(node.leftChild == null && node.rightChild == null) { System.out.println("{" + node.cData + ":" + myPath + "}"); hmCodeTable.put(String.valueOf(node.cData), myPath); return; } if(node.leftChild != null) { sPaths[0] = myPath + "0"; getPaths(node.leftChild,sPaths[0]); } if(node.rightChild != null) { sPaths[1] = myPath + "1"; getPaths(node.rightChild,sPaths[1]); } return; } /*UNIT*UNIT *测试代码 *遍历队列但不删除元素 */ private static void traveQueue(PriorityQueue<HuffmanTree> list) { HuffmanTree[] trees = new HuffmanTree[list.size()]; int i = 0; while(list.size() > 0) { HuffmanTree tree = list.peek(); System.out.print(tree.getRoot().cData + ":" + tree.getNumber() + " "); list.remove(); trees[i] = tree; i++; } System.out.println(); for(HuffmanTree theTree:trees) { list.add(theTree); } } }
本站声明:网站内容来源于网络,如有侵权,请联系我们https://www.qiquanji.com,我们将及时处理。
微信扫码关注
更新实时通知