哈夫曼樹

百度百科定義

給定n個權(quán)值作為n個葉子結(jié)點递宅,構(gòu)造一棵二叉樹,若帶權(quán)路徑長度達(dá)到最小苍狰,稱這樣的二叉樹為最優(yōu)二叉樹办龄,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權(quán)路徑長度最短的樹淋昭,權(quán)值較大的結(jié)點離根較近俐填。

image.png

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

  • 左子樹
  • 右子樹
  • 父節(jié)點
  • 權(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;
        }
    }

哈夫曼樹構(gòu)建

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

  • (1) 將w1、w2材失、…痕鳍,wn看成是有n 棵樹的森林(每棵樹僅有一個結(jié)點);
  • (2) 在森林中選出兩個根結(jié)點的權(quán)值最小的樹合并,作為一棵新樹的左笼呆、右子樹熊响,且新樹的根結(jié)點權(quán)值為其左、右子樹根結(jié)點權(quán)值之和诗赌;
  • (3 )從森林中刪除選取的兩棵樹汗茄,并將新樹加入森林;
  • (4) 重復(fù)(2)铭若、(3)步洪碳,直到森林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹叼屠。

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

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


    步驟1.png
  • 3 把新節(jié)點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);
    }

哈夫曼樹遍歷

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


我們遍歷這棵樹


image.png

步驟

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

隊列{114}

  • 2 隊列l(wèi)ist不為空冷离,彈出隊頭吵冒,輸出
TreeNode node = list.pop();
System.out.println(node.data);
  • 3 讀取隊頭node 的左孩子,和右孩子西剥,如果孩子不為空痹栖,孩子加入隊列
if (node.leftChild != null) {
    list.offer(node.leftChild);
    }
if (node.rightChild != null) {
    list.offer(node.rightChild);
    }
  • 現(xiàn)在隊列的數(shù)據(jù){46,68}
  • 彈出46 ,隊尾加入46的左孩子瞭空,右孩子揪阿,隊列現(xiàn)在的數(shù)據(jù){68,21,25}
  • 彈出68 ,隊尾加入68的左孩子咆畏,右孩子南捂,隊列現(xiàn)在的數(shù)據(jù){21,25,32,36}
  • 彈出21 ,隊尾加入21的左孩子旧找,右孩子溺健,隊列現(xiàn)在的數(shù)據(jù){25,32,36,8,13}
  • 彈出25 ,隊尾加入25的左孩子钮蛛,右孩子鞭缭,25沒有左右孩子,隊列現(xiàn)在的數(shù)據(jù){32,36,8,13}
  • 彈出32 魏颓,隊尾加入32的左孩子岭辣,右孩子,隊列現(xiàn)在的數(shù)據(jù){36,8,13,13,19}
  • 彈出36 甸饱,隊尾加入32的左孩子沦童,右孩子,36沒有左右孩子,隊列現(xiàn)在的數(shù)據(jù){8,13,13,19}
  • 彈出8 ,隊尾加入8的左孩子搞动,右孩子, 沒有左右孩子,隊列現(xiàn)在的數(shù)據(jù){13,13,19}
  • 彈出13 渣刷,隊尾加入13的左孩子鹦肿,右孩子,隊列現(xiàn)在的數(shù)據(jù){13,19,6,7}
  • 彈出13 辅柴,隊尾加入13的左孩子箩溃,右孩子,隊列現(xiàn)在的數(shù)據(jù){19,6,7}
  • 彈出19 碌嘀,隊尾加入19的左孩子涣旨,右孩子,隊列現(xiàn)在的數(shù)據(jù){6,7}
  • 彈出6股冗,隊尾加入6的左孩子霹陡,右孩子,隊列現(xiàn)在的數(shù)據(jù){7}
  • 彈出7止状,隊尾加入7的左孩子烹棉,右孩子,隊列現(xiàn)在的數(shù)據(jù){2,5}
  • 彈出2怯疤,隊尾加入2的左孩子浆洗,右孩子,隊列現(xiàn)在的數(shù)據(jù){5}
  • 彈出5集峦,隊尾加入5的左孩子伏社,右孩子,隊列現(xiàn)在的數(shù)據(jù){}
  • 隊列為空塔淤,遍歷結(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());
        }
    }

測試

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)容合作請聯(lián)系作者
  • 序言:七十年代末摘昌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子高蜂,更是在濱河造成了極大的恐慌第焰,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妨马,死亡現(xiàn)場離奇詭異挺举,居然都是意外死亡,警方通過查閱死者的電腦和手機烘跺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門湘纵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人滤淳,你說我怎么就攤上這事梧喷。” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵铺敌,是天一觀的道長汇歹。 經(jīng)常有香客問我,道長偿凭,這世上最難降的妖魔是什么产弹? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮弯囊,結(jié)果婚禮上痰哨,老公的妹妹穿的比我還像新娘。我一直安慰自己匾嘱,他們只是感情好斤斧,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霎烙,像睡著了一般撬讽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悬垃,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天锐秦,我揣著相機與錄音,去河邊找鬼盗忱。 笑死酱床,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的趟佃。 我是一名探鬼主播扇谣,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼闲昭!你這毒婦竟也來了罐寨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤序矩,失蹤者是張志新(化名)和其女友劉穎鸯绿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體簸淀,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡瓶蝴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了租幕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舷手。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖劲绪,靈堂內(nèi)的尸體忽然破棺而出男窟,到底是詐尸還是另有隱情盆赤,我是刑警寧澤歉眷,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布牺六,位于F島的核電站,受9級特大地震影響汗捡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凉唐,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一簿训、第九天 我趴在偏房一處隱蔽的房頂上張望米间。 院中可真熱鬧强品,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昧诱,卻和暖如春晓淀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盏档。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工凶掰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜈亩。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓懦窘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稚配。 傳聞我的和親對象是個殘疾皇子奶赠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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