C/C++ 哈夫曼樹與哈夫曼編碼

我的筆記,僅供複習函數

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最小。因而用哈夫曼樹來進行建樹,就可使編碼代價最小。

相關文章
相關標籤/搜索