1. 程式人生 > 其它 >資料結構09 哈夫曼樹

資料結構09 哈夫曼樹

這一篇要總結的是樹中的哈夫曼樹(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);
        }
    }
}

執行結果: