我的筆記,僅供複習函數
1.哈夫曼樹
1.1 定義:ui
- 帶權路徑長度(WPL):設二叉樹有n個葉子結點,每一個葉子結點帶有權值W(k),從根結點到每一個葉子結點的長度爲L(k),則每一個葉子結點的帶權路徑長度之和就是:WPL = W(1)*L(1) + W(2)*L(2) + ... + W(k)*L(k)
- 最優二叉樹或哈夫曼樹:WPL最小的二叉樹
如上面三棵樹,第三顆的WPL最小,因此它是哈夫曼樹。編碼
1.2 構造哈夫曼樹spa
1.2.1 思想:每次把權值最小的兩棵樹合併,合併後的新樹做爲新的結點再與其餘結點進行比較。指針
1.2.2 僞代碼:code
(1)從全部結點的集合A中選出權值最小的兩個結點blog
(2)將這兩個結點合併成一棵樹,同時從集合A刪除這兩個結點,新結點的權值爲兩個子樹權值的和字符串
(3)將新結點放回集合A,重複(1),直到A爲空字符編碼
1.2.3 代碼實例:class
typedef struct TreeNode *HuffmanTree; struct TreeNode{ int Weight; HuffmanTree Left, Right; } HuffmanTree Huffman( MinHeap H ) { /* 假設H->Size個權值已經存在H->Elements[]->Weight裏 */ int i; HuffmanTree T; BuildMinHeap(H); /*將H->Elements[]按權值調整爲最小堆*/ for (i = 1; i < H->Size; i++) { /*作H->Size-1次合併*/ T = malloc( sizeof( struct TreeNode) ); /*創建新結點*/ T->Left = DeleteMin(H); /*從最小堆中刪除一個結點,做爲新T的左子結點*/ T->Right = DeleteMin(H); /*從最小堆中刪除一個結點,做爲新T的右子結點*/ T->Weight = T->Left->Weight+T->Right->Weight; /*計算新權值*/ Insert( H, T ); /*將新T插入最小堆*/ } T = DeleteMin(H); return T; }
1.2.4 代碼分析:
以上代碼是用C語言的指針來建樹,同時用最小堆來存儲結點,方便每次找權值最小的結點。其中的MinHeap是最小堆的結構體的指針;DeleteMin()函數是對最小堆進行刪除最小權值結點,同時返回該結點地址的操做。
1.3 哈夫曼樹的特色:
- 沒有度爲1的結點
- n個葉子結點的哈夫曼樹共2n-1個結點
- 哈夫曼樹的任意非葉結點的左右子樹交換後還是哈夫曼樹
1.4 對同一組權值是否存在不一樣的兩顆哈夫曼樹? 答:是。
2.哈夫曼編碼
2.1 背景:給定一段字符串,如何對字符進行編碼,可使得該字符串的編碼存儲空間最少?
2.2 分析:共三種方法
- 用等長ASCII編碼:58 ×8 = 464位;
- 用等長3位編碼:58 ×3 = 174位;
- 不等長編碼:出現頻率高的字符用的編碼短些,出現頻率低的字符則能夠編碼長些。
2.3 若是要進行不等長編碼須要考慮的問題:
- 如何避免編碼的二義性(即如何讓編碼意義惟一肯定,不會有歧義)
- 如何讓編碼代價最小(讓頻率高的編碼儘可能短,頻率低的能夠長點)
2.3.1 避免二義性:
使任何字符的編碼都不是其餘字符編碼的前綴碼(例如,將0編碼爲1,那麼其餘字符編碼就不能以1開頭)。用二叉樹進行編碼,左分支定爲0,右分支爲1,字符只放在葉結點上。這樣就能夠肯定任一個字符編碼不是另外一個的前綴。
如上圖a、u、x、z的編碼分別爲:a:00,u:01,x:10,z:11
2.3.2 編碼代價最小:
這樣問題就從如何讓編碼代價最小變成了如何讓二叉樹WPL最小。因而用哈夫曼樹來進行建樹,就可使編碼代價最小。