霍夫曼樹(最優二叉樹)的實現

狗喵的發表於2020-10-20

一、相關概念

1.節點的路徑及路徑長度

路徑:在樹中,一個節點向下到達另一個節點的通路,稱為路徑。
路徑長度:路徑中經歷的分支數。
在這裡插入圖片描述
圖中節點1到節點4的路徑就是:1->2,2->4。路徑長度為2。

2.節點的帶權路徑長度

在樹中,可以定義某個節點的權值,從根節點到此節點的路徑長度與此節點的權值的乘積就是該節點的帶權路徑長度。
例如:上圖中若定義節點4的權值為4,則其帶權路徑長度為4*2=8。

3.樹的帶權路徑長度

樹中所有葉子節點的帶權路徑之和稱為樹的帶權路徑長度,簡稱WPL(weighted path length)。

4.霍夫曼樹

擁有相同葉子節點的所有樹中,WPL最小的樹稱為霍夫曼樹,又稱最優二叉樹。
在這裡插入圖片描述
上圖中右面的就為霍夫曼樹(未畫出所有情況)。

二、構建步驟與圖解

1.構建步驟

目標:給定一個數列arr,其中元素對應葉子節點的權值,以此構建霍夫曼樹。

  1. 將數列中各元素看成只有根節點的樹,按權值對各樹從小到大排序。
  2. 以其中最小的兩顆樹為左右子樹構建成一顆新的樹t,t的權值為兩子樹權值之和。
  3. 在數列中用t取代其左右子樹,再對剩下的樹按權值進行排序,並迴圈1,2步驟。
  4. 直到數列中只剩下一個元素,霍夫曼樹就構建成了。

2.圖解

以arr={2,3,4,12,7,6}為例。

1.排序{2,3,4,6,7,12},並取出前兩個。
在這裡插入圖片描述
2.變為{4,6,7,12,5},排序{4,5,6,7,12},取出前兩個。
在這裡插入圖片描述
3.變為{6,7,12,9},排序{6,7,9,12},取出前兩個。
在這裡插入圖片描述
4.變為{9,12,13},取出前兩個。
在這裡插入圖片描述
4.變為{13,21},取出前兩個。
在這裡插入圖片描述

5.4.變為{34},結束。

三、程式碼實現

1.建立節點類:

class Node implements Comparable<Node> {
    int value;
    Node left;
    Node right;

    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;//從小到大排序
    }

    //前序遍歷方法
    public void preOrderTraversal(Node root){
        if(root == null) return;
        System.out.println(root);
        preOrderTraversal(root.left);
        preOrderTraversal(root.right);
    }
}

2.建立霍夫曼樹

public static Node huffmanTree(int[] arr){
        List<Node> nodes = new ArrayList<>();
        for (int data : arr) {//用arr建立多個node節點,存到nodes中
            nodes.add(new Node(data));
        }
        
        Node newNode = null;
        //構建霍夫曼樹
        while(nodes.size() > 1){
            Collections.sort(nodes);
            //System.out.println(nodes);
            Node left = nodes.remove(0);
            Node right = nodes.remove(0);
            newNode = new Node(left.value + right.value);
            newNode.left = left;
            newNode.right = right;
            nodes.add(newNode);
        }
        return newNode;
    }
}

3.全部程式碼

public class HuffmanTree {
    public static void main(String[] args) {
        int[] arr = {2, 3, 4, 12, 7, 6};
        Node root = huffmanTree(arr);
        root.preOrderTraversal(root);
    }
    
    public static Node huffmanTree(int[] arr){
        List<Node> nodes = new ArrayList<>();
        for (int data : arr) {
            nodes.add(new Node(data));
        }
        Node newNode = null;
        
        while(nodes.size() > 1){
            Collections.sort(nodes);
            //System.out.println(nodes);
            Node left = nodes.remove(0);
            Node right = nodes.remove(0);
            newNode = new Node(left.value + right.value);
            newNode.left = left;
            newNode.right = right;
            nodes.add(newNode);
        }
        return newNode;
    }
}

class Node implements Comparable<Node> {
    int value;
    Node left;
    Node right;

    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;//從小到大排序
    }

    //前序遍歷方法
    public void preOrderTraversal(Node root){
        if(root == null) return;
        System.out.println(root);
        preOrderTraversal(root.left);
        preOrderTraversal(root.right);
    }
}

相關文章