先從文本讀取字符,統計字符出現的次數用map保存,然后根據詞頻計算每個字符的哈弗曼編碼,哈弗曼樹的建立過程就是每次取兩個權值最小的節點生成新的節點,根據建立的哈弗曼樹計算每個字符的哈弗曼編碼,如果當前節點在父節點的左邊,當前節點的編碼為0,否則為1,最后從上往下記錄哈弗曼編碼.代碼如下,代碼注釋很詳細.
HuffmanTree.java
import java.util.*;
public class HuffmanTree {
class Node {
int val;
int weight;
Node left;
Node right;
Node parent;
Node(int weight) {
this.weight = weight;
}
}
/**
* 計算哈弗曼編碼
*
* @param counter key為對應的字符,value為字符出現的次數
* @return 返回一個map, key為字符, value為對應的哈弗曼編碼
*/
public Map<Integer, String> getHuffmanCode(Map<Integer, Integer> counter) {
//根據權重排序建立一個優先隊列來保存節點,方便每次取最小的兩個節點
PriorityQueue<Node> priorityQueue = new PriorityQueue<>(
new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return o1.weight - o2.weight;
}
}
);
//用數組保存哈弗曼樹, 哈弗曼數的度為0的節點為counter.size(), 度為1的節點為0
//度為2的葉子節點為counter.size() - 1
Node[] huffmanTree = new Node[counter.size() * 2 - 1];
int index = 0;
//把葉子節點放在數組前面的counter.size()個位置
for (Map.Entry<Integer, Integer> entry : counter.entrySet()) {
huffmanTree[index] = new Node(entry.getValue());
huffmanTree[index].val = entry.getKey();
priorityQueue.add(huffmanTree[index]);
index++;
}
//每次從優先隊列中取權重最小的兩個節點生成新的父節點
for (int i = counter.size(); i < huffmanTree.length; i++) {
Node n1 = priorityQueue.poll();
Node n2 = priorityQueue.poll();
huffmanTree[i] = new Node(n1.weight + n2.weight);
huffmanTree[i].left = n1;
huffmanTree[i].right = n2;
n1.parent = huffmanTree[i];
n2.parent = huffmanTree[i];
priorityQueue.add(huffmanTree[i]);
}
//levelTraverse(huffmanTree);
//保存每個字符的哈弗曼編碼
Map<Integer, String> res = new HashMap<>();
//計算每個字符的哈弗曼值
StringBuilder huffmanCode = new StringBuilder();
Node parent, curNode;
for (int i = 0; i < counter.size(); i++) {
huffmanCode.setLength(0);
curNode = huffmanTree[i];
parent = huffmanTree[i].parent;
//從下往上計算huffmanTree[i].val的的哈弗曼編碼
while (parent != null) {
//當前節點在父節點的左邊,哈弗曼編碼為0,在右邊為1
if (parent.left == curNode) {
huffmanCode.append("0");
} else {
huffmanCode.append("1");
}
curNode = parent;
parent = parent.parent;
}
//因為是從下往上生成的,所以需要逆置一下
huffmanCode.reverse();
res.put(huffmanTree[i].val, huffmanCode.toString());
}
return res;
}
/**
* 從上往下打印哈弗曼數的權值,調試用
*
* @param huffamanTree
*/
private void levelTraverse(Node[] huffamanTree) {
for (int i = huffamanTree.length - 1; i >= 0; i--) {
System.out.print(huffamanTree[i].weight + " ");
}
System.out.println("");
}
}
TestHuffmanTree.java
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class TestHuffmanTree {
public static void main(String[] args) throws IOException {
//統計每個字符出現的次數
BufferedReader bis = null;
try {
bis = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Map<Integer, Integer> counter = new HashMap<>();
while (true) {
int k = bis.read();
if (k == -1) {
break;
}
counter.putIfAbsent(k, 0);
counter.put(k, counter.get(k) + 1);
}
System.out.println("字符頻率為:");
System.out.println(counter);
HuffmanTree huffmanTree = new HuffmanTree();
Map<Integer, String> huffmanCodes = huffmanTree.getHuffmanCode(counter);
System.out.println("哈弗曼編碼為:");
System.out.println(huffmanCodes);
}
}
input.txt
abbcccddddeeeee