哈夫曼編碼(Huffman Coding)是一種編碼方式,也稱爲「赫夫曼編碼」,是David A. Huffman1952年發明的一種構建極小多餘編碼的方法。
在計算機數據處理中,霍夫曼編碼使用變長編碼表對源符號進行編碼,出現頻率較高的源符號採用較短的編碼,出現頻率較低的符號採用較長的編碼,使編碼以後的字符串字符串的平均長度 、指望值下降,以達到無損壓縮數據的目的。
舉個例子,如今咱們有一字符串:html
this is an example of a huffman treenode
這串字符串有36個字符,若是按普通方式存儲這串字符串,每一個字符佔據1個字節,則共須要36 * 1 * 8 = 288bit。
通過分析咱們發現,這串字符串中各字母出現的頻率不一樣,若是咱們可以按以下編碼:git
字母 | 頻率 | 編碼 | --- | 字母 | 頻率 | 編碼 |
---|---|---|---|---|---|---|
space | 7 | 111 | s | 2 | 1011 | |
a | 4 | 010 | t | 2 | 0110 | |
e | 4 | 000 | l | 1 | 11001 | |
f | 3 | 1101 | o | 1 | 00110 | |
h | 2 | 1010 | p | 1 | 10011 | |
i | 2 | 1000 | r | 1 | 11000 | |
m | 2 | 0111 | u | 1 | 00111 | |
n | 2 | 0010 | x | 1 | 10010 |
編碼這串字符串,只須要:
(7+4+4)x3 + (3+2+2+2+2+2+2)x4 + (1+1+1+1+1+1)x 5 = 45+60+30 = 135bit
編碼這串字符串只須要135bit!單單這串字符串,就壓縮了288-135 = 153bit。github
那麼,咱們如何獲取每一個字符串的編碼呢?這就須要哈夫曼樹了。
源字符編碼的長短取決於其出現的頻率,咱們把源字符出現的頻率定義爲該字符的權值。數組
哈夫曼又稱最優二叉樹。是一種帶權路徑長度最短的二叉樹。它的定義以下。數據結構
假設有n個權值{w1,w2,w3,w4...,wn},構造一棵有n個節點的二叉樹,若樹的帶權路徑最小,則這顆樹稱做哈夫曼樹。這裏面涉及到幾個概念,咱們由一棵哈夫曼樹來解釋
函數
路徑與路徑長度:從樹中一個節點到另外一個節點之間的分支構成了兩個節點之間的路徑,路徑上的分支數目稱做路徑長度。若規定根節點位於第一層,則根節點到第H層的節點的路徑長度爲H-1.如樹b:100到60 的路徑長度爲1;100到30的路徑長度爲2;100到20的路徑長度爲3。post
樹的帶權路徑長度:樹的帶權路徑長度爲全部葉子節點的帶權路徑長度之和,稱爲WPL。樹a的WPL = 2*(10+20+30+40) = 200 ;樹b的WPL = 1x40+2x30+3x10+3x20 = 190.而哈夫曼樹就是樹的帶權路徑最小的二叉樹。性能
/*哈夫曼樹的節點定義*/ template <typename T> struct HuffmanNode { HuffmanNode(T k,HuffmanNode<T>*l=nullptr,HuffmanNode<T>* r=nullptr) :key(k),lchild(l), rchild(r){} ~HuffmanNode(){}; T key; //節點的權值 HuffmanNode<T>* lchild; //節點左孩 HuffmanNode<T>* rchild; //節點右孩 };
template <typename T> class Huffman { public: void preOrder(); //前序遍歷哈夫曼樹 void inOrder(); //中序遍歷哈夫曼樹 void postOrder(); //後序遍歷哈夫曼樹 void creat(T a[], int size); //建立哈夫曼樹 void destory(); //銷燬哈夫曼樹 void print(); //打印哈夫曼樹 Huffman(); ~Huffman(){}; private: void preOrder(HuffmanNode<T>* pnode); void inOrder(HuffmanNode<T>* pnode); void postOrder(HuffmanNode<T>*pnode); void print(HuffmanNode<T>*pnode); void destroy(HuffmanNode<T>*pnode); private: HuffmanNode<T>* root; //哈夫曼樹根節點 deque<HuffmanNode<T>*> forest;//森林 };
假設有n個權值,則構造出的哈夫曼樹有n個葉子節點.n個權值記爲{w1,w2,w3...wn},哈夫曼樹的構造過程爲:測試
圖一的樹b爲一棵哈夫曼樹,它的葉子節點爲{10,20,30,40},以這4個權值構建樹b的過程爲:
這個過程很編碼實現爲:
/*建立哈夫曼樹*/ template<typename T> void Huffman<T>::creat(T a[],int size) { for (int i = 0; i < size; i++) //每一個節點都做爲一個森林 { //爲初始序列的元素構建節點。每一個節點做爲一棵樹加入森林中。 HuffmanNode<T>* ptr = new HuffmanNode<T>(a[i],nullptr,nullptr); forest.push_back(ptr); } for (int i = 0; i < size - 1; i++) { //排序,以選出根節點權值最小兩棵樹 sort(forest.begin(), forest.end(), [](HuffmanNode<T>* a, HuffmanNode<T>*b){return a->key< b->key; }); HuffmanNode<T>*node = new HuffmanNode<T>(forest[0]->key + forest[1]->key, forest[0], forest[1]); //構建新節點 forest.push_back(node); //新節點加入森林中 forest.pop_front(); //刪除兩棵權值最小的樹 forest.pop_front(); } root = forest.front(); forest.clear(); };
其餘操做在前幾篇博文中都有介紹過,這裏就再也不囉嗦,能夠在文章底部連接取得完整的工程源碼。
這裏貼出測試時須要的代碼:
/*打印哈夫曼樹*/ template<typename T> void Huffman<T>::print(HuffmanNode<T>* pnode) { if (pnode != nullptr) { cout << "當前結點:" << pnode->key<<"."; if (pnode->lchild != nullptr) cout << "它的左孩子節點爲:" << pnode->lchild->key << "."; else cout << "它沒有左孩子."; if (pnode->rchild != nullptr) cout << "它的右孩子節點爲:" << pnode->rchild->key << "."; else cout << "它沒有右孩子."; cout << endl; print(pnode->lchild); print(pnode->rchild); } };
咱們構建上圖中的哈夫曼樹,它的四個權值分別爲{10,20,30,40}:
測試代碼:
int _tmain(int argc, _TCHAR* argv[]) { Huffman<int> huff; int a[] = { 10,20,30,40 }; huff.creat(a, 4); //構建一棵哈夫曼樹 huff.print(); //打印節點間關係 getchar(); return 0; }
測試結果:
當前結點:100.它的左孩子節點爲:40.它的右孩子節點爲:60. 當前結點:40.它沒有左孩子.它沒有右孩子. 當前結點:60.它的左孩子節點爲:30.它的右孩子節點爲:30. 當前結點:30.它沒有左孩子.它沒有右孩子. 當前結點:30.它的左孩子節點爲:10.它的右孩子節點爲:20. 當前結點:10.它沒有左孩子.它沒有右孩子. 當前結點:20.它沒有左孩子.它沒有右孩子.
根據節點關係能夠畫出以下二叉樹,正是上面咱們構建的哈夫曼樹。
爲{10,20,30,40}這四個權值構建了哈夫曼編碼後,咱們能夠由以下規則得到它們的哈夫曼編碼:
(字母)權值 | 編碼 |
---|---|
10 | 100 |
20 | 101 |
30 | 11 |
40 | 0 |
因而可知,出現頻率越高的字母(也即權值越大),其編碼越短。這便使編碼以後的字符串的平均長度、指望值下降,從而達到無損壓縮數據的目的。
哈夫曼樹完整代碼:https://github.com/huanzheWu/Data-Structure/blob/master/Huffman/Huffman/Huffman.h
更多數據結構C++實現代碼:https://github.com/huanzheWu/Data-Structure
原創文章,轉載請註明出處:http://www.cnblogs.com/QG-whz/p/5175485.html