Java 最优二叉树的哈夫曼算法的简单实现
一、哈夫曼编码算法简介
哈夫曼编码(Huffman coding)是一种无损压缩编码,广泛用于数据的压缩和传输。哈夫曼编码利用字符出现的频率进行编码,出现频率高的字符对应的编码短,反之出现频率低的字符对应的编码长,从而达到减少数据存储空间和传输带宽的目的。
哈夫曼编码的核心思想是构造哈夫曼树,将出现频率高的字符作为叶子节点,出现频率低的字符作为离叶子节点较近的节点,以此构造一个最优二叉树来实现编码。
二、哈夫曼编码算法过程
下面是哈夫曼编码算法的详细步骤:
1. 统计频率
遍历待编码的字符串,统计每个字符出现的频率,并存储到频率表中。
2. 构造哈夫曼树
通过频率表构造哈夫曼树:将出现频率低的字符作为左子树,出现频率高的字符作为右子树,从而构造出最优二叉树。
3. 生成编码表
以根节点为起点,向下递归遍历整个哈夫曼树,对于每个叶子节点记录它们的哈夫曼编码,生成编码表。
4. 编码数据
利用生成的编码表对数据进行编码,将原字符串中的字符依次转换成对应的哈夫曼编码串。
5. 解码数据
利用生成的哈夫曼树对哈夫曼编码串进行解码,将哈夫曼编码串转换成原字符串中的字符。
三、Java 实现哈夫曼编码算法
下面是使用 Java 语言实现哈夫曼编码算法的简单示例:
import java.util.*;
public class HuffmanCoding {
/**
* 统计字符出现的频率
*
* @param data 待编码的字符串
* @return Map<Character, Integer> 每个字符出现的频率
*/
public static Map<Character, Integer> getFrequency(String data) {
Map<Character, Integer> frequency = new HashMap<Character, Integer>();
for (int i = 0; i < data.length(); i++) {
char c = data.charAt(i);
if (frequency.containsKey(c)) {
frequency.put(c, frequency.get(c) + 1);
} else {
frequency.put(c, 1);
}
}
return frequency;
}
/**
* 构造哈夫曼树
*
* @param frequency 每个字符出现的频率
* @return Node 哈夫曼树的根节点
*/
public static Node buildHuffmanTree(Map<Character, Integer> frequency) {
PriorityQueue<Node> queue = new PriorityQueue<Node>();
// 初始化节点队列
for (Character c : frequency.keySet()) {
queue.offer(new Node(c, frequency.get(c), null, null));
}
// 构建哈夫曼树
while (queue.size() > 1) {
Node leftNode = queue.poll();
Node rightNode = queue.poll();
Node parent = new Node(null, leftNode.frequency + rightNode.frequency, leftNode, rightNode);
queue.offer(parent);
}
// 返回根节点
return queue.poll();
}
/**
* 遍历哈夫曼树,生成编码表
*
* @param root 哈夫曼树的根节点
* @return Map<Character, String> 每个字符对应的哈夫曼编码
*/
public static Map<Character, String> getCodingMap(Node root) {
Map<Character, String> codingMap = new HashMap<Character, String>();
getCode(root, "", codingMap);
return codingMap;
}
private static void getCode(Node node, String code, Map<Character, String> codingMap) {
if (node.isLeaf()) {
codingMap.put(node.data, code);
return;
}
getCode(node.left, code + "0", codingMap);
getCode(node.right, code + "1", codingMap);
}
/**
* 对数据进行编码
*
* @param data 待编码的字符串
* @param codingMap 每个字符对应的哈夫曼编码
* @return String 哈夫曼编码串
*/
public static String encode(String data, Map<Character, String> codingMap) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.length(); i++) {
sb.append(codingMap.get(data.charAt(i)));
}
return sb.toString();
}
/**
* 对哈夫曼编码串进行解码
*
* @param code 哈夫曼编码串
* @param root 哈夫曼树的根节点
* @return String 解码后的字符串
*/
public static String decode(String code, Node root) {
StringBuilder sb = new StringBuilder();
Node node = root;
for (int i = 0; i < code.length(); i++) {
char c = code.charAt(i);
node = (c == '0') ? node.left : node.right;
if (node.isLeaf()) {
sb.append(node.data);
node = root;
}
}
return sb.toString();
}
/**
* 节点类
*/
static class Node implements Comparable<Node> {
Character data; // 节点值(叶子节点才有值)
Integer frequency; // 节点出现频率
Node left; // 左子节点
Node right; // 右子节点
public Node(Character data, Integer frequency, Node left, Node right) {
this.data = data;
this.frequency = frequency;
this.left = left;
this.right = right;
}
/**
* 判断节点是否为叶子节点
*/
public boolean isLeaf() {
return left == null && right == null;
}
/**
* 优先队列中的比较方法
*/
@Override
public int compareTo(Node node) {
return frequency - node.frequency;
}
}
public static void main(String[] args) {
String data = "hello world, i am a Java program.";
Map<Character, Integer> frequency = getFrequency(data);
Node root = buildHuffmanTree(frequency);
Map<Character, String> codingMap = getCodingMap(root);
String code = encode(data, codingMap);
String text = decode(code, root);
System.out.println("原数据:" + data);
System.out.println("编码后:" + code);
System.out.println("解码后:" + text);
}
}
四、示例
- 编码示例:
待编码字符串:hello world, i am a Java program.
统计字符出现频率:{r=2, a=4, ,=6, h=1, d=1, e=1, J=1, g=1, l=3, o=4, i=1, m=2, n=1, p=1, W=1, .=1}
构造哈夫曼树:
*
/
21
/ \
/ \
8 *
/ \ / \
/ \ / \
3 * 17 4
/ \
/ \
7 10
/ \ / \
2 5 7 3
生成编码表:
{ =111, a=00, .=1000, d=1111, e=1001, g=1011, h=11011, i=10100, J=110100, l=010, m=1100, n=10101, o=01, p=110101, r=011, W=110001}
编码结果:
`110111010100101100111110 …
- 解码示例:
待解码字符串: 110111010100101100111110 …
(同上)
哈夫曼树(同上)
解码结果:
hello world, i am a Java program.
通过上述示例,我们可以清晰地了解哈夫曼编码算法的实现过程和原理,并且也能够准确地对数据进行编码和解码。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 最优二叉树的哈夫曼算法的简单实现 - Python技术站