普通树与二叉树的相互转化及哈夫曼树的了解
普通樹與二叉樹的相互轉(zhuǎn)化及哈夫曼樹的了解
二叉樹與普通樹的轉(zhuǎn)化
二叉樹的種種特性使得它更便于處理,如果能將普通樹轉(zhuǎn)化成二叉樹就好了。
普通樹 -> 二叉樹
回憶孩子兄弟表示法,有第一孩子域(左孩子),還有左孩子的兄弟域。孩子表示法表示的樹很容易轉(zhuǎn)化成二叉樹,所以只需把樹先轉(zhuǎn)為孩子兄弟表示法的樣子,再調(diào)整層次結(jié)構(gòu)就可以得到二叉樹。
- 加線。在所有兄弟結(jié)點之間加一條線。
- 去線。只保留父結(jié)點與第一個孩子(左孩子)的連線,與其他孩子的連線刪掉。
- 調(diào)整層次,結(jié)點的左孩子依然為左孩子,左孩子的兄弟全變成了結(jié)點右孩子。
森林也可以轉(zhuǎn)化成二叉樹,所謂森林就是樹的集合。
- 把森林的每一棵樹轉(zhuǎn)成二叉樹
- 以某一棵樹作為起始樹,下一棵樹的根結(jié)點作為右孩子連接到上一課樹的根結(jié)點。直到處理完最后一棵樹。
二叉樹 -> 普通樹
- 加線。如果某個結(jié)點存在左孩子,則將左孩子的右結(jié)點,其右結(jié)點的右孩子...也就是一直深入到?jīng)]有右孩子,將這些結(jié)點與父結(jié)點連線。
- 去線。刪除所有結(jié)點與其右孩子的連線。
- 調(diào)整結(jié)構(gòu),讓原本某結(jié)點的右孩子與該結(jié)點處于一個水平線,則他們成為了兄弟。
二叉樹也能變成森林。
從根結(jié)點開始,如果存在右孩子,斷開與右孩子的連線。接著處理上分離后的二叉樹的根結(jié)點,如果存在右孩子,斷開連線....如此反復(fù),直到某結(jié)點無右孩子。然后將得到的若干二叉樹轉(zhuǎn)為普通樹。
哈夫曼樹與哈夫曼編碼
哈夫曼編碼用在數(shù)據(jù)壓縮領(lǐng)域。我們先來看哈夫曼樹。
哈夫曼樹的構(gòu)造
樹中一個結(jié)點到另一個結(jié)點之間的分支構(gòu)成了路徑,路徑上分支的數(shù)目稱為路徑長度。樹的路徑長度就是:根結(jié)點到每一個結(jié)點的路徑長度之和。再把一棵二叉樹的葉子結(jié)點帶上權(quán)值,定義結(jié)點的帶權(quán)路徑長度為:根結(jié)點到葉子結(jié)點的路徑長度 * 葉子結(jié)點的權(quán)值。那么樹的帶權(quán)路徑長度為所有葉子結(jié)點的帶權(quán)路徑長度之和。
比如有結(jié)點數(shù)為n的二叉樹,有m個葉子結(jié)點。權(quán)值分別是w1, w2, w3...根結(jié)點到它們的路徑長度分別是m1, m2, m3...則m1*w1 + m2*w2 + m3*w3 +...+ mm*wm就是這棵樹的帶權(quán)路徑長度。
比如二叉樹a,結(jié)點A的路徑長度為1,結(jié)點D的路徑長度為4...于是該樹的帶權(quán)路徑長度就是:5*1 + 15*2 +40*3 +30*4 + 10*4 = 315
二叉樹b的帶權(quán)路徑長度為:40*2 + 5*3 + 15*3 +30*2 + 10*2 = 220
由于n個結(jié)點的二叉樹有多種可能的形態(tài),葉子結(jié)點的個數(shù)、結(jié)點的路徑長度都各不相同,這些樹的帶權(quán)路徑長度有的很大有的很小。我們的目的是要找出使樹的帶權(quán)路徑長度最小的二叉樹,這樣的二叉樹稱為哈夫曼樹。哈夫曼樹的形態(tài)也不唯一(比如某棵哈夫曼樹作鏡面對稱),但是這些樹帶權(quán)路徑長度一定是唯一值。
上圖就是一棵哈夫曼樹,它的帶權(quán)路徑長度為40*1 + 30*2 + 15*3 + 5*4 + 10*4 = 205,比前面兩種情況的值都小。那么這棵樹是怎么來的呢?幸好有簡單的構(gòu)造方法。
- 先將帶權(quán)的葉子結(jié)點按照權(quán)值從小到大排序,以上圖為例子就是{A: 5, E: 10, B: 15, D: 30, C: 40}
- 取出前兩個結(jié)點,新增一個N1結(jié)點作為這兩個結(jié)點的父結(jié)點,權(quán)值小的作為N1的左孩子,權(quán)值稍大的作為右孩子,并將N1的權(quán)值為設(shè)置為這兩個結(jié)點的權(quán)值之和,如圖N1的權(quán)值為A和E的權(quán)值之和15。
- 已取出的葉子結(jié)點從序列中刪除,并將新增的N1結(jié)點插入到序列中合適的位置,保持序列有序。然后重復(fù)上一步驟。直到所有葉子的結(jié)點的都已被取出,至此就完成了哈夫曼樹的構(gòu)造。(完成的圖就是上圖那樣)
哈夫曼編碼
試想將一段字符通過網(wǎng)絡(luò)傳輸給別人,如BADCADFEED,由于其中只有ABCDEF六個字母,用三位的二進制數(shù)就可以完全表示。每個字母被編碼成以下表格所示。
| 二進制編碼 | 000 | 001 | 010 | 011 | 100 | 101 |
這樣上面的BADCADFEED編碼后就是001000011010000011101100100011,長度是30。對方接收到這一長串再根據(jù)上表,每三位代表一個字母,解碼出真正的序列。
現(xiàn)在我們嘗試用哈夫曼編碼對這段數(shù)據(jù)進行壓縮。假如我們有一段字符要進行傳輸,這段字符中共出現(xiàn)6個字母,每個字母出現(xiàn)的頻率是{A: 27, B: 8, C: 15, D: 15, E: 30, F: 5}。根據(jù)上面介紹的哈夫曼樹的構(gòu)造,可以得到下面的左圖。
現(xiàn)在將結(jié)點到左孩子的路徑權(quán)值改為0,到右孩子的權(quán)值改為1,從根結(jié)點到葉子結(jié)點所經(jīng)過的路徑組成的0、1序列就是該字符的編碼。舉比如字符D被編碼為00,可以列出每個字符的編碼序列。
| 二進制編碼 | 01 | 1001 | 101 | 00 | 11 | 1000 |
可以看出二進制位數(shù)參差不齊了,頻率高的二進制位數(shù)少,頻率低的所需位數(shù)就多。BADCADFEED現(xiàn)在被編碼成了1001010010101001000111100,長度是25。比上面少了5個字符,這說明我們可以用更少的數(shù)據(jù)量傳輸內(nèi)容,卻不丟失語意。我們確實成功壓縮了數(shù)據(jù)。
對方接收到這一長串,再根據(jù)上表解碼出真正的序列。不過在解碼的時候,由于表中的編碼各個字符的二進制位數(shù)不是固定,有的3位數(shù)、有的4位數(shù)....如果某個編碼序列是另外一個編碼序列的前綴,在解碼的時候我們就不能確定這到底是哪個字符。所以在編碼的時候一定要避免這樣的情況發(fā)生,編碼方案需要滿足:任意字符的編碼都不是其他任意字符編碼的前綴,這種編碼稱為前綴編碼。
by @sunhaiyu
2017.9.14
轉(zhuǎn)載于:https://www.cnblogs.com/sun-haiyu/p/7521466.html
總結(jié)
以上是生活随笔為你收集整理的普通树与二叉树的相互转化及哈夫曼树的了解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uniapp引入阿里巴巴矢量图标库
- 下一篇: bjui简单了解