在一棵樹中,從乙個結點往下可以達到的孩子或孫子結點之間的通路,稱為路徑。通路中分支的數目稱為路徑長度。若規定根結點的層數為1,則從根結點到第l層結點的路徑長度為l-1。
若將樹中結點賦給乙個有著某種含義的數值,則這個數值稱為該結點的權。結點的帶權路徑長度為:從根結點到該結點之間的路徑長度與該結點的權的乘積。
樹的帶權路徑長度規定為所有葉子結點的帶權路徑長度之和,記為wpl。
若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(huffman tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。
為了構造權值集合為的哈夫曼樹,哈夫曼提出了乙個構造演算法,這個演算法就是哈夫曼演算法。基本思路如下:
(1)根據給定的n個權值,構造具有n棵擴充二叉樹的森林f = ,其中每棵擴充二叉樹ti只有乙個帶權值wi的根結點,其左右子樹均為空。
(2)重複以下步驟,直到f中只剩下一棵樹為止。
①在f中選取兩棵根結點的權值最小的子樹分別作為左右子樹構造一棵新的二叉樹。置新的二叉樹的根結點的權值為其左右子樹上根結點權值之和。
②在f中刪去這兩棵二叉樹。
③把新的二叉樹加入f。
最後得到的就是哈夫曼樹。
下圖是哈夫曼樹構造的**過程:
注:從哈夫曼演算法可知,實際上構造出來的哈夫曼樹可以不止一棵。
這裡用c++中的結構型別描述哈夫曼樹中的結點
template
struct htnode
;此處將lchild、rchild、parent依舊稱為」指標」,他們實際上是三個整形指示器,分別指示結點在陣列中的下標。因為c++語言陣列的下界是0,故用-1來表示空指標。設定 parent 域有兩個作用,其一是便於查詢某個結點的雙親,其二是可通過判斷parent的值是否為-1來區分根結點和非根結點。
哈夫曼演算法的初始狀態是乙個初始森林,共有n課二叉樹。同時每一次的合併,都會產生乙個新結點,合併 n - 1 次共產生 n - 1 個新結點,他們都是具有兩個孩子的分支結點。所以最終求得的哈夫曼樹共有2n-1個結點。因此可以用大小為2n-1的一維陣列來存放哈夫曼樹中的結點,其儲存結構如下:
const int n = 50; //葉子的最多數目
const int m = 2 * n - 1; //樹中結點的總數
template
class huffmantree
;用ht陣列存放哈夫曼樹,對於具有 n 個葉子結點的哈夫曼樹,總共有 2n - 1 個結點。其演算法的基本思路是,n 個葉子結點(存放在 ht[0] ~ ht[n - 1] 中)只有data和weight域值,先將所有 2n - 1個結點的parent、rchild、lchild域置初值為 -1 。處理每個非葉子結點ht[i](存放在 ht[n] ~ ht[2n - 2]中)如下:從 ht[0] ~ ht[i - 2] 中找出根結點(即其parent域為-1)最小的兩個結點ht[lnode]和ht[rnode],將他們作為 ht[i]的左右子樹,ht[lnode]和ht[rnode]的雙親結點置為ht[i],並且ht[i].weight = ht[lnode].weight + ht[rnode].weight。如此操作直到所有2n - 1 個非葉子結點處理完畢。具體演算法如下:
template
huffmantree::huffmantree(htnode ht, int n) else if (ht[k].weight < min2) }}
ht[lnode].parent = ht[rnode].parent = i;
ht[i].weight = ht[lnode].weight + ht[rnode].weight;
ht[i].lchild = lnode;
ht[i].rchild = rnode;
}for (i = 0; i < 2 * n - 1; ++i)
}哈夫曼樹最經典的應用是在通訊領域。經哈夫曼編碼的資訊消除了冗餘資料,極大地提高了通訊通道的傳輸效率。
哈夫曼編碼是根據每個符號出現的頻率,將其儲存的定長編碼轉換成變長編碼。為出現概率較高的字元指定較短的碼字,而為出現概率較低的字元指定較長的碼字,可以明顯提高傳輸的平均效能。
表:哈夫曼編碼
符號概率
定長編碼
變長編碼
a 0.12
0001111
b 0.40
001 0
c0.15
010110
d 0.08
0111110
e 0.25
10010
表中的兩種二進位制編碼可以用二叉樹表示,如圖,其中,二叉樹的葉結點標記字元,由根結點沿著二叉樹路徑下行,左分支標記為0,右分支標記為1,則每條從根結點到葉結點的路徑唯一表示了該葉結點的二進位制編碼。
圖:兩種編碼方案的二叉樹示意圖
平均編碼長度最小的編碼稱為最優編碼。哈夫曼樹的加權路徑長度就是相應編碼的平均編碼長度。我們稱這樣得到的編碼為哈夫曼編碼。根據前面介紹的哈夫曼樹的性質,我們知道哈夫曼編碼是最優的二進位制編碼。
實現哈夫曼編碼的演算法可以分為兩個部分:
(1)構造哈夫曼樹,此演算法在前面已給出。
(2)在哈夫曼樹上求葉結點的編碼。
求哈夫曼編碼,實質上就是在已建立的哈夫曼樹中,從葉結點開始,沿葉結點的雙親鏈域回退到根結點,每回退一步,就走過了哈夫曼樹的乙個分支,從而得到一位哈夫曼編碼值,由於乙個字元的哈夫曼編碼是從根結點到相應葉結點所經過的路徑上各分支所組成的」0」、」1」序列,因此先得到的分支**為所求的編碼的低位碼,後得到的分支**為所求編碼的高位碼,可以設定陣列huffmancode用來存放各個字元的哈夫曼資訊。
//接著上面的哈夫曼樹類定義
struct hcode
;template
void huffmantree::createhcode(hcode hcd, int n)
hc.start++; //start指向哈夫曼編碼的最開始字元
hcd[i] = hc;}}
資料結構 哈夫曼樹 哈夫曼編碼
哈夫曼樹又稱最優樹 二叉樹 是一類帶權路徑最短的樹。構造這種樹的演算法最早是由哈夫曼 huffman 1952年提出,這種樹在資訊檢索中很有用。結點之間的路徑長度 從乙個結點到另乙個結點之間的分支數目。樹的路徑長度 從樹的根到樹中每乙個結點的路徑長度之和。結點的帶權路徑長度 從該結點到樹根之間的路徑...
哈夫曼編碼 哈夫曼樹 (資料結構)
哈夫曼編碼,又稱霍夫曼編碼,是一種編碼方式,哈夫曼編碼是可變字長編碼 vlc 的一種。huffman於1952年提出一種編碼方法,該方法完全依據字元出現概率來構造異字頭的平均長度最短的碼字,有時稱之為最佳編碼,一般就叫做huffman編碼 有時也稱為霍夫曼編碼 include include inc...
資料結構 哈夫曼樹
哈夫曼樹是二叉樹的一種。被稱為最優二叉樹。實際應用中最重要的是帶權路徑長度。樹的路徑長度 樹中每個結點的路徑長度之和。權 附加在樹節點上,表示出現的概率。樹的帶權路徑長度 所有葉子結點帶權長度之和。看例項 的結點路徑長度 從d到 a的路徑,共走了兩條邊,所以為2。樹中的葉子結點有 d,e和 f。結點...