資料結構09 哈夫曼樹
阿新 • • 發佈:2022-05-04
這一篇要總結的是樹中的哈夫曼樹(Huffman Tree),我想從以下幾點對其進行總結:
1、什麼是哈夫曼樹
2、如何構建哈夫曼樹
3、哈夫曼編碼
4、程式碼實現
1、什麼是哈夫曼樹
什麼是哈夫曼樹呢?
哈夫曼樹是一種帶權路徑長度最短的二叉樹,也稱為最優二叉樹。下面用一幅圖來說明。
它們的帶權路徑長度分別為:
圖a: WPL=5*2+7*2+2*2+13*2=54
圖b: WPL=5*3+2*3+7*2+13*1=48
可見,圖b的帶權路徑長度較小,我們可以證明圖b就是哈夫曼樹(也稱為最優二叉樹)。
2、如何構建哈夫曼樹
一般可以按下面步驟構建:
(1)將所有左,右子樹都為空的節點作為根節點。
(2)在森林中選出兩棵根節點的權值最小的樹作為一棵新樹的左,右子樹,且置新樹的根節點的權值為其左,右子樹上根節點的權值之和。注意,左子樹的權值應小於右子樹的權值。
(3)從森林中刪除這兩棵樹,同時把新樹加入到森林中。
(4)重複2、3步驟,直到森林中只有一棵樹為止,此樹便是哈夫曼樹。
下面是構建哈夫曼樹的圖解過程:
3、哈夫曼編碼
利用哈夫曼樹求得的用於通訊的二進位制編碼稱為哈夫曼編碼。樹中從根到每個葉子節點都有一條路徑,對路徑上的各分支約定指向左子樹的分支表示”0”碼,指向右子樹的分支表示“1”碼,取每條路徑上的“0”或“1”的序列作為各個葉子節點對應的字元編碼,即是哈夫曼編碼。
就拿上圖例子來說:
A,B,C,D對應的哈夫曼編碼分別為:111,10,110,0
用圖說明如下:
4、程式碼實現
程式碼:
節點類 Node.java
/** * 節點類 */ public class Node implements Comparable { private int value; private Node leftChild; private Node rightChild; public Node(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } 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 int compareTo(Object o) { Node that = (Node) o; double result = that.value - that.value; return result > 0 ? 1 : result == 0 ? 0 : -1; } }
HuffmanTreeBuilder.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 哈夫曼樹構造類
*/
public class HuffmanTreeBuilder {
public static void main(String[] args) {
List<Node> nodes = Arrays.asList(
new Node(1),
new Node(3),
new Node(5),
new Node(7)
);
Node node = HuffmanTreeBuilder.build(nodes);
printTree(node);
}
/**
* 構造哈夫曼樹
*
* @param nodes 節點集合
* @return 構造出來的樹的根節點
*/
private static Node build(List<Node> nodes) {
nodes = new ArrayList<Node>(nodes);
sortList(nodes);
while (nodes.size() > 1) {
createAndReplace(nodes);
}
return nodes.get(0);
}
/**
* 組合兩個權值最小節點,並在節點列表中用它們的父節點替換它們
*
* @param nodes 節點集合
*/
private static void createAndReplace(List<Node> nodes) {
Node left = nodes.get(0);
Node right = nodes.get(1);
Node parent = new Node(left.getValue() + right.getValue());
parent.setLeftChild(left);
parent.setRightChild(right);
nodes.remove(0);
nodes.remove(0);
nodes.add(parent);
sortList(nodes);
}
/**
* 將節點集合由小到大排序
*
* @param nodes
*/
private static void sortList(List<Node> nodes) {
Collections.sort(nodes);
}
/**
* 列印樹結構,顯示的格式是node(left,right)
*
* @param node
*/
public static void printTree(Node node) {
Node left = null;
Node right = null;
if (node != null) {
System.out.print(node.getValue());
left = node.getLeftChild();
right = node.getRightChild();
System.out.println("(" + (left != null ? left.getValue() : " ") + "," + (right != null ? right.getValue() : " ") + ")");
}
if (left != null) {
printTree(left);
}
if (right != null) {
printTree(right);
}
}
}
執行結果: