哈夫曼樹(shù)

百度百科定義

給定n個(gè)權(quán)值作為n個(gè)葉子結(jié)點(diǎn),構(gòu)造一棵二叉樹(shù),若帶權(quán)路徑長(zhǎng)度達(dá)到最小,稱這樣的二叉樹(shù)為最優(yōu)二叉樹(shù),也稱為哈夫曼樹(shù)(Huffman Tree)。哈夫曼樹(shù)是帶權(quán)路徑長(zhǎng)度最短的樹(shù),權(quán)值較大的結(jié)點(diǎn)離根較近。

image.png

哈夫曼樹(shù)數(shù)據(jù)結(jié)構(gòu)

  • 左子樹(shù)
  • 右子樹(shù)
  • 父節(jié)點(diǎn)
  • 權(quán)重
  • 數(shù)據(jù)
public static class TreeNode<T> implements Comparable<TreeNode<T>>{
        T data;
        int weight;
        TreeNode leftChild;
        TreeNode rightChild;
        TreeNode parent;
        
        public TreeNode(T data, int wei) {
            this.data = data;
            this.weight = wei;
            leftChild = null;
            rightChild = null;
            parent = null;
        }

        @Override
        public int compareTo(TreeNode<T> o) {
            if (this.weight > o.weight) {
                return -1;
            } else if (this.weight < o.weight) {
                return 1;
            }
            return 0;
        }
    }

哈夫曼樹(shù)構(gòu)建

假設(shè)有n個(gè)權(quán)值,則構(gòu)造出的哈夫曼樹(shù)有n個(gè)葉子結(jié)點(diǎn)。 n個(gè)權(quán)值分別設(shè)為 w1、w2、…、wn,則哈夫曼樹(shù)的構(gòu)造規(guī)則為:

  • (1) 將w1、w2、…,wn看成是有n 棵樹(shù)的森林(每棵樹(shù)僅有一個(gè)結(jié)點(diǎn));
  • (2) 在森林中選出兩個(gè)根結(jié)點(diǎn)的權(quán)值最小的樹(shù)合并,作為一棵新樹(shù)的左、右子樹(shù),且新樹(shù)的根結(jié)點(diǎn)權(quán)值為其左、右子樹(shù)根結(jié)點(diǎn)權(quán)值之和;
  • (3 )從森林中刪除選取的兩棵樹(shù),并將新樹(shù)加入森林;
  • (4) 重復(fù)(2)、(3)步,直到森林中只剩一棵樹(shù)為止,該樹(shù)即為所求得的哈夫曼樹(shù)。

舉個(gè)例子
有這樣權(quán)值數(shù)組 {13,6,8,19,2,36,5,25 }

  • 1 排序
    {2,5,6,8,13,19,25,36}
  • 2 在這些數(shù)中 選擇兩個(gè)最小的權(quán)值,作為一棵新樹(shù)node的左右節(jié)點(diǎn)(小的在做,大的在右),node的權(quán)值為其左、右子樹(shù)根結(jié)點(diǎn)權(quán)值之和


    步驟1.png
  • 3 把新節(jié)點(diǎn)node的權(quán)值加入數(shù)組,重新排序
    {7,6,8,13,19,25,36}
    重復(fù)步驟2
步驟2.png
  • 4 重復(fù)步驟3
    排序后的權(quán)值數(shù)組 {8,13,13,19,25,36}


    image.png

    排序后的權(quán)值數(shù)組{13,19,21,25,36}


    image.png

    排序后的權(quán)值數(shù)組{21,25,32,36}
    image.png

    排序后的權(quán)值數(shù)組 {32,36,46}
    image.png

    排序后的權(quán)值數(shù)組{46,68}


    image.png

    排序后的權(quán)值數(shù)組{114}

public TreeNode createHaffManTree(ArrayList<TreeNode> list) {
        while (list.size() > 1) {
            Collections.sort(list);
            TreeNode left = list.get(list.size()-1);
            TreeNode right = list.get(list.size()-2);
            TreeNode parent = new TreeNode("p", left.weight + right.weight);
            parent.leftChild = left;
            left.parent = parent;
            parent.rightChild = right;
            right.parent = parent;
            list.remove(left);
            list.remove(right);
            list.add(parent);
        }
        root = list.get(0);
        return list.get(0);
    }

哈夫曼樹(shù)遍歷

層次遍歷 :使用隊(duì)列實(shí)現(xiàn)層次遍歷


我們遍歷這棵樹(shù)


image.png

步驟

  • 1 把根節(jié)點(diǎn)加入到隊(duì)列]
    list.offer(root);

隊(duì)列{114}

  • 2 隊(duì)列l(wèi)ist不為空,彈出隊(duì)頭,輸出
TreeNode node = list.pop();
System.out.println(node.data);
  • 3 讀取隊(duì)頭node 的左孩子,和右孩子,如果孩子不為空,孩子加入隊(duì)列
if (node.leftChild != null) {
    list.offer(node.leftChild);
    }
if (node.rightChild != null) {
    list.offer(node.rightChild);
    }
  • 現(xiàn)在隊(duì)列的數(shù)據(jù){46,68}
  • 彈出46 ,隊(duì)尾加入46的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){68,21,25}
  • 彈出68 ,隊(duì)尾加入68的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){21,25,32,36}
  • 彈出21 ,隊(duì)尾加入21的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){25,32,36,8,13}
  • 彈出25 ,隊(duì)尾加入25的左孩子,右孩子,25沒(méi)有左右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){32,36,8,13}
  • 彈出32 ,隊(duì)尾加入32的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){36,8,13,13,19}
  • 彈出36 ,隊(duì)尾加入32的左孩子,右孩子,36沒(méi)有左右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){8,13,13,19}
  • 彈出8 ,隊(duì)尾加入8的左孩子,右孩子, 沒(méi)有左右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){13,13,19}
  • 彈出13 ,隊(duì)尾加入13的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){13,19,6,7}
  • 彈出13 ,隊(duì)尾加入13的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){19,6,7}
  • 彈出19 ,隊(duì)尾加入19的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){6,7}
  • 彈出6,隊(duì)尾加入6的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){7}
  • 彈出7,隊(duì)尾加入7的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){2,5}
  • 彈出2,隊(duì)尾加入2的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){5}
  • 彈出5,隊(duì)尾加入5的左孩子,右孩子,隊(duì)列現(xiàn)在的數(shù)據(jù){}
  • 隊(duì)列為空,遍歷結(jié)束
image.png
public void showHaffman(TreeNode root) {
        LinkedList<TreeNode> list = new LinkedList<>();
        ArrayList<TreeNode> arrayList = new ArrayList<>();
        list.offer(root);
        while (!list.isEmpty()) {
            TreeNode node = list.pop();
            System.out.println(node.data);
            if (node.leftChild != null) {
                list.offer(node.leftChild);
            }
            if (node.rightChild != null) {
                list.offer(node.rightChild);
            }
        }
    }

獲取哈夫曼編碼

image.png
  • 8 的哈夫曼編碼:000
  • 6 的哈夫曼編碼:0010
  • 2 的哈夫曼編碼:00110
  • 5 的哈夫曼編碼:00111
  • 25 的哈夫曼編碼:01
  • 13 的哈夫曼編碼:100
  • 19 的哈夫曼編碼:101
  • 19 的哈夫曼編碼:11
image.png
public void getCode(TreeNode node) {
        Stack<String> stack = new Stack<>();
        TreeNode tNode = node;
        while (tNode != null && tNode.parent != null) {
            // left 0, right 1
            if (tNode.parent.leftChild == tNode) {
                stack.push("0");
            } else if (tNode.parent.rightChild == tNode) {
                stack.push("1");
            }
            tNode = tNode.parent;
        }
        while (!stack.isEmpty()) {
            System.out.print(stack.pop());
        }
    }

測(cè)試

public static void main(String[] args) {
        ArrayList<TreeNode> list = new ArrayList<>();
        TreeNode<String> node = new TreeNode("good", 54);
        list.add(node);
        list.add(new TreeNode("morning", 10));
        list.add(new TreeNode("afternoon", 20));
        list.add(new TreeNode("hello", 100));
        list.add(new TreeNode("hi", 200));
        HaffmanTree tree = new HaffmanTree();
        tree.createHaffManTree(list);
        tree.showHaffman(tree.root);
        tree.getCode(node);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容