1. 程式人生 > 其它 >赫夫曼數(Wpl最小的樹)

赫夫曼數(Wpl最小的樹)

13.4 赫夫曼樹

基本介紹:

  1. 給定 n 個權值作為 n 個葉子節點,構造一棵二叉樹,若該樹的帶權路徑長度(wpl)達到最小,成這樣的二叉樹為 最優二叉樹, 也成為 赫夫曼樹(Huffman Tree),還有的書翻譯為 霍夫曼樹
  2. 赫夫曼樹是帶權路徑長度最短的樹,權值較大的節點離跟結點較近。

重要概念:

  1. 路徑和路徑長度:在一棵樹中,從一個節點往下可以達到的孩子或者孫子節點之間的通路,稱為路徑。通路中分支的數目稱為路徑長度。若規定跟結點層數為1,則從跟結點到第L層結點的路徑長度為 L-1
  2. 結點的權及帶權路徑長度:若將樹中結點賦給一個有著某種含義的數值,則這個數值稱為該節點的權。結點的帶權路徑長度為,從根節點到該節點的路徑長度與該節點的權的乘積

例如:結點13的權值是13,帶權路徑長度為(13 * ( 3 - 1) )= 26, 3是因為13結點在第三層,減一 跟人理解是Java語言是從0 開始計數的而不是從1開始

  1. 樹的帶權路徑長度:樹的帶權路徑長度規定為所有葉子節點的帶權路徑長度之和,記為 WPL(weighted path length),權值越大的節點離跟結點越近的二叉樹書才是最優二叉樹
  2. WPL最小的就是赫夫曼樹

需求:

將數列{13, 7, 8, 3, 29, 6, 1},要求轉成一顆赫夫曼樹。

思路:

  1. 先從小到大進行排序,將每一個數據,每個資料都是一個結點,每個節點可以看成是一顆最簡單的二叉樹
  2. 取出跟結點權值最小的兩顆二叉樹
  3. 組成一顆新的二叉樹,該新的二叉樹的根節點的權值是前兩克二叉樹跟結點權值的和
  4. 在將這顆新的二叉樹,以根節點的權值大小再次排序,不斷重複 1 - 2 - 3 - 4的步驟,直到數列中,所有的資料都被處理,就得到一顆赫夫曼樹
package huffmantree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HuffmanTree {
    public static void main(String[] args) {
        int[] arr = {13, 7, 8, 3, 29, 6, 1};
        Node root = createHuffmanTree(arr);
        System.out.println("赫夫曼樹:");
        preOrder(root);


    }
    // 編寫一個前序遍歷的方法
    public static void preOrder(Node root){
        if (root != null){
            root.preOrder();
        }else {
            System.out.println("該樹是空樹");
        }
    }
    // 建立赫夫曼樹的方法
    public static Node createHuffmanTree(int[] arr){
        // 第一步,為了操作方便
        // 1. 遍歷arr 陣列
        // 2. 將 arr 的每個元素構建成 node
        // 3. 將node 放入到ArrayList中
        List<Node> nodes = new ArrayList<>();
        for (int value : arr){
            nodes.add(new Node(value));
        }
        while(nodes.size() > 1){
            // 排序,從小到大
            Collections.sort(nodes);
            System.out.println("nodes = " + nodes);

            // 取出跟結點權值最小的兩顆二叉樹
            // 1. 取出權值最小的結點(二叉樹)
            Node leftNode = nodes.get(0);
            // 2. 取出第二小的結點
            Node rightNode = nodes.get(1);

            // 3. 構建一棵新的二叉樹
            Node parent = new Node(leftNode.value + rightNode.value);
            parent.left = leftNode;
            parent.right = rightNode;

            // 4. 從ArrayList刪除處理過的二叉樹
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            // 5. 將parent 接待你加入到 nodes
            nodes.add(parent);

            System.out.println("第"+nodes.size()+"次處理後;" + nodes);
        }
        // 返回赫夫曼樹的root結點
        return nodes.get(0);

    }
}
// 建立結點
// 為了讓Node 物件支援排序 Collections集合排序
// 讓node實現Comparable介面
class Node implements Comparable<Node>{
    int value; // 結點的權值
    Node left; // 指向左子節點
    Node right; // 指向右子節點

    // 前序遍歷
    public void preOrder(){
        System.out.println(this);
        if (this.left != null){
            this.left.preOrder();
        }
        if (this.right != null){
            this.right.preOrder();
        }
    }
    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }


    @Override
    public int compareTo(Node o) {
        // 從小到大排序
        return this.value - o.value;
        // 從大到小排序
        // return -(this.value - o.value);
    }
}