赫夫曼树介绍与实现
来源:互联网 发布:js时间戳转换工具 编辑:程序博客网 时间:2024/06/06 00:07
先了解几个概念:
- 树节点的带权路径长度为从该节点到树根之间的路径长度与节点上权的乘积。
- 树的带权路径长度为树中所有叶子节点的带权路径长度之和。
- 带权路径长度最小的二叉树称做最优二叉树或赫夫曼树。
构造赫夫曼树的通常方法:
1. 根据给定的 n 个权值 {
2. 在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为其左右子树上根节点的权值之和。
3. 在F中删除这两棵树,同时将新得到的二叉树加入F中。
4. 重复(2)和(3),直到F只含一棵树为止。这棵树便是赫夫曼树。
霍夫曼编码:
霍夫曼编码(Huffman Coding)是一种编码方法,霍夫曼编码是可变字长编码(VLC)的一种。霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现机率的方法得到的,出现机率高的字母使用较短的编码,反之出现机率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。
例如使用赫夫曼编码对文本进行编码:
每种字符在电文中出现的次数为
下面给出实现代码:
package huffman;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.HashMap;import java.util.LinkedList;import java.util.List;import java.util.Map.Entry;/*** @ClassName: Huffman* @author xhf* @date 2017年11月18日* @Description: 霍夫曼编码问题*/public class Huffman { /** * @Fields rootNode : 赫夫曼树的根节点 */ private Node rootNode; /** * @Fields nodeList : 存放统计后的节点 */ private List<Node> nodeList = new LinkedList<>(); /** * @Title: createHuffmanTree * @param @return * @return Node * @throws * @Description: 根据给定的字符出现的次数和字符信息构造霍夫曼树 */ public void createHuffmanTree(String data) { /** * 统计字符串中字符出现的次数比重 */ this.statistics(data); List<Node> tempNodeList = new ArrayList<>(); tempNodeList.addAll(nodeList); /** * 根据霍夫曼树构造规则构造霍夫曼树 */ while (tempNodeList.size() > 1) { List<Node> minList = this.getMinList(tempNodeList); Node minOne = minList.get(0); Node minTwo = minList.get(1); tempNodeList.remove(minOne); tempNodeList.remove(minTwo); Node newNode = new Node(); minOne.setCode((byte)0); minTwo.setCode((byte)1); newNode.setLeftChild(minOne); minOne.setParent(newNode); newNode.setRightChild(minTwo); minTwo.setParent(newNode); newNode.setWeight(minOne.getWeight() + minTwo.getWeight()); tempNodeList.add(newNode); } this.rootNode = tempNodeList.size() == 1 ? tempNodeList.get(0) : null; } /** * @Title: statistics * @param @param data * @return void * @throws * @Description: 统计字符串中各字符出出现的次数 */ public void statistics(String data) { char[] datachars = data.toCharArray(); HashMap<Character, Integer> dataMap = new HashMap<>(); int sum = 0; for (char c : datachars) { if (dataMap.get(c) == null) { dataMap.put(c, 1); } else { dataMap.put(c, dataMap.get(c)+1); } sum += 1; } for (Entry<Character, Integer> e : dataMap.entrySet()) { Node node = new Node(); node.setData(e.getKey()); node.setWeight((double)e.getValue() / sum); nodeList.add(node); } } /** * @Title: getMinList * @param @param nodeList * @param @return * @return List<Node> * @throws * @Description: 获取当前集合中的最小的两个节点 */ public List<Node> getMinList(List<Node> nodeList) { List<Node> minList = new ArrayList<>(); Node minOne = null; Node minTwo = null; for (Node node : nodeList) { if (minOne == null) { minOne = node; continue; } if (minTwo == null) { if (node.getWeight() >= minOne.getWeight()) { minTwo = node; } else { minTwo = minOne; minOne = node; } continue; } { if (node.getWeight() < minOne.getWeight()) { minTwo = minOne; minOne = node; } else if (node.getWeight() < minTwo.getWeight()) { minTwo = node; } continue; } } minList.add(minOne); minList.add(minTwo); return minList; } /** * @Title: decode * @param @return * @return String * @throws * @Description: 根据构造的霍夫曼树得到霍夫曼编码 */ public String encode(String data) { this.createHuffmanTree(data); char[] chars = data.toCharArray(); StringBuffer sb = new StringBuffer(); for (char c : chars) { for (Node n : nodeList) { if (n.getData() == c) { sb.append(this.getNodeCode(n)); } } } return sb.toString(); } /** * @Title: decode * @param @param code * @param @return * @return String * @throws * @Description: 根据霍夫曼树将赫夫曼编码转为初始文本 */ public String decode(String code) { if (rootNode == null) return ""; char[] chars = code.toCharArray(); StringBuffer sb = new StringBuffer(); Node finder = rootNode; for (char c : chars) { if (c == '0') { finder = finder.getLeftChild(); } else { finder = finder.getRightChild(); } if (finder.getLeftChild() == null) { sb.append(finder.getData()); finder = rootNode; } } return sb.toString(); } /** * @Title: getNodeCode * @param @param n * @param @return * @return String * @throws * @Description: 从叶子节点向上遍历得到字符的编码 */ public String getNodeCode(Node n) { StringBuffer sb = new StringBuffer(); while (n != null && n.getCode() != null) { sb.append(n.getCode()); n = n.getParent(); } return sb.reverse().toString(); } public static void main(String[] args) { Huffman hm = new Huffman(); String code = hm.encode("System.out.println(decode);"); String decode = hm.decode(code); System.out.println(code); System.out.println(decode); }}
/*** @ClassName: Node* @author xhf* @date 2017年11月18日* @Description: 用于构造二叉树的节点*/class Node { /** * @Fields weight : 权重 */ private Double weight; /** * @Fields code : 该节点的对应 0 1。左孩子0, 右孩子1 */ private Byte code; /** * @Fields data : 节点中存储的字符 */ private Character data; /** * @Fields leftChild : 左孩子 */ private Node leftChild; /** * @Fields rightChild : 右孩子 */ private Node rightChild; /** * @Fields parent : 父节点 */ private Node parent; public Double getWeight() { return weight; } public void setWeight(Double weight) { this.weight = weight; } public Byte getCode() { return code; } public void setCode(Byte code) { this.code = code; } public Character getData() { return data; } public void setData(Character data) { this.data = data; } public Node getLeftChild() { return leftChild; } public void setLeftChild(Node leftChild) { this.leftChild = leftChild; } public Node getRightChild() { return rightChild; } public void setRightChild(Node rightChild) { this.rightChild = rightChild; } public Node getParent() { return parent; } public void setParent(Node parent) { this.parent = parent; }}
- 赫夫曼树介绍与实现
- Murmurhash介绍与实现
- Murmurhash介绍与实现
- 红黑树的介绍与实现
- Kruskal算法介绍与实现
- Prim算法介绍与实现
- LRU缓存介绍与实现
- AVL树介绍与实现
- Android硬件加速介绍与实现
- RPC介绍与代码实现
- 网络通讯的介绍、与实现
- AVL树介绍与实现
- RSA算法的介绍与实现
- cp命令的介绍与实现
- LRU缓存介绍与实现 (Java)
- BFS/DFS算法介绍与实现
- PHP接口的介绍与实现
- atoi函数介绍与代码实现
- gtest 简单实例
- Error:java: Compilation failed: internal java compiler error
- TCP三次握手与四次挥手
- 3.【练习题】构造方法与重载 定义一个网络用户类,要处理的信息有用户ID、用户密码、email地址。拓展:判断密码长度
- 异常
- 赫夫曼树介绍与实现
- 关于上一次小游戏的进阶设计
- 剑指offer每日一刷-2017年11月19日
- 11.19
- iOS多线程 一一 多线程简介
- SpringMVC之国际化插件-yellowcong
- 用Construct2再制作游戏
- K近邻算法详解
- 【Java.NIO】Selector,及SelectionKey