huffman編碼是重要的編碼方式之一。作為一名程式員,應該要做到融會貫通,因此我在網上搜些資料看,發現幾乎沒有適合我等菜鳥級的文章,不是沒有圖解,就是不說明為什麼要引入huffman編碼,稀裡糊塗,於是自己整理了一下,歡迎諸君拍磚。
Huffman是一種無損壓縮方法:所謂無損就是不會有資訊丟失,可以100%恢複原訊號,像奈奎斯特恢複就是有損了;所謂壓縮,就是可以佔有更少的儲存空間。
比如要發送三個字元:S1,S2,S3。我們可以這樣編碼:00,01,10。當然這是小資料,如果要發送的圖片或是視頻呢?比如一副高清圖片,10M,在區域網路上即使可以達到理論的10Mbps,也要1s,沒法滿足視頻的最低幀頻24fps。因此壓縮非常重要,當然採用的壓縮很多,huffman只是早期的一種而已,這就是Huffman的背景。
現在來看看它的原理:先不聯絡最優二叉樹那套東西。基本原理是根據字元出現的機率確定其碼子。即高機率的少碼字,低機率的多碼字。 舉個例子吧:
圖 1
結合:
A B C D E 5個字元及其出現的機率。過程很簡單:
First: 從上到下,根據機率由大到小排列;
Second: 倆倆合并,先合并機率低的,比如D E 機率最低,合并之,得到機率0.1,再尋找機率最低的倆部分,發現是C 和 DE,合并之,合并後,
標出新的 機率值 ,中紅色圈起來的部分,直到合并完全;
Third: 標出0或1。中藍色部分,低機率的給0(1也可以),機率相等的,就0和1隨意了;
Last: 得到編碼值:A:1,B:10;C:100; D:1000; E:0000。
整個編碼過程完畢。以上就是Huffman編碼的大體過程。好了,感受完其最基本的過程。我們再來完成最優二叉樹(赫夫曼樹)的代碼編寫,分為倆部分:第一部分為基本概念,第二部分為:代碼的實現,並有測試結果。
Huffman 樹狀目錄:帶權路徑最短的樹。所謂帶權路徑,就是從該節點到樹根的長度與節點上的權的乘積
圖 2
那麼樹的帶權路徑就是所有節點的帶權路徑之和,公式表示:
如:套公式:6*1+4*2+1*3+2*3
有了一開始部分的介紹,huffman二叉樹的構造就很簡單了,步驟:
1,找到權重最小的倆節點,合并最為一個新的節點,比如上面D,E合并,得到一個新的節點,暫且叫 做DE,則D,E作為子節點,DE作為父節點;
2,以DE代替D和E,DE的權重為D和E之和,重新第一步,直到所有節點都加入到二叉樹中。
3,對於權重小的節點附0(或1),對於權重大的節點附1(或0)。
如把圖1huffman的計算過程變成二叉樹的 圖形如下:
圖 3
讀者,把一開始的圖1 ,旋轉過來跟圖 2 是一樣的,因為原理一樣。注意:huffman分葉節點為n,需要2*n-1個節點來構成二叉樹,樹的節點的度為2(除去分葉節點)。
第二部分:以一個例子開始: 權重W={5,29,7,8,14,23,3,11}.n=8.此例子為嚴蔚敏第159頁的例子。
上代碼:
/*Coder Information e_mail:shenganbeiyang@163.com QQ:501968942*/#ifndef HUFF_H#define HUFF_H#include<string>//定義一個結構體,包括節點的父節點和左右子節點//字元的個數const int maxnum=30;typedef struct Tnode{ char val; int weight; int parent; int lchild; int rchild;}huffmanNode ,*huffmanTree;class huffmanCoding{public://初始化最優二叉樹(哈夫曼樹)huffmanCoding(huffmanNode * hn,int length); //編碼過程 void Selection(int);void Encode(int); //解碼過程// stringdeCode();//deconstruction function~huffmanCoding(){for(int i=0;i<length;++i)delete HC[i];delete [] HC;}; char** HC;//儲存編碼資訊private:huffmanNode InitData[maxnum];//char** HC;//儲存編碼資訊int length;};//constructor function.初始化二叉樹inline huffmanCoding::huffmanCoding(huffmanNode * hn,int length_){ HC=new char*[length_];//(sizeof(char*)*length);//new (char*)[length]; length=length_; int i; for( i=0;i<length;++i) { InitData[i]=hn[i];InitData[i].parent=-1;InitData[i].rchild=-1;InitData[i].lchild=-1; } for(int j=i;j<2*length;++j) {InitData[j].lchild=-1;InitData[j].rchild=-1;InitData[j].weight=-1;InitData[j].val='0';InitData[j].parent=-1; }}//編碼huffman二叉樹inline void huffmanCoding::Selection(int codedlength){int pos_1,pos_2;pos_1=pos_2=-1;int i; for( i=0;i<codedlength;++i){if(InitData[i].parent==-1){pos_1=i; break; }} for(int k=pos_1+1;k<codedlength;++k){if(InitData[k].weight<InitData[pos_1].weight&&InitData[k].parent==-1){ pos_1=k;}} for(int j=0;j<codedlength;++j) {if(InitData[j].parent==-1&&j!=pos_1){pos_2=j; break;} } for(int j=pos_2+1;j<codedlength;++j) { if(InitData[j].weight<InitData[pos_2].weight&&InitData[j].parent==-1&&j!=pos_1) { pos_2=j; } } if(InitData[pos_1].weight<InitData[pos_2].weight) { InitData[codedlength].lchild=pos_1; InitData[codedlength].rchild=pos_2; InitData[pos_1].parent=codedlength; InitData[pos_2].parent=codedlength; InitData[codedlength].weight=InitData[pos_1].weight+InitData[pos_2].weight; } else { InitData[codedlength].lchild=pos_2; InitData[codedlength].rchild=pos_1; InitData[pos_1].parent=codedlength; InitData[pos_2].parent=codedlength; InitData[codedlength].weight=InitData[pos_1].weight+InitData[pos_2].weight; } }inline void huffmanCoding::Encode(int len){ for(int i=len;i<2*len-1;++i) { Selection(i); } char*dc=new char[length]; for(int m=0;m<length;++m) dc[m]='\0'; int start=0; int tmp; int p; for(int m=0;m<length;++m) { for(tmp=m,p=InitData[m].parent;p!=-1;tmp=p,p=InitData[p].parent) { if(InitData[p].lchild==tmp) dc[start++]='0'; else dc[start++]='1'; } dc[start]='\0'; HC[m]=new char[length]; start=0; strcpy(HC[m],dc); }delete [] dc;}#endif
測試代碼:
#include"huff.h"#include<iostream>using namespace std;int main(){ int i=0;huffmanTree ht=new huffmanNode[8];for(;i<8;++i){ ht[i].val='a'+i;}ht[0].weight=5;ht[1].weight=29;ht[2].weight=7;ht[3].weight=8;ht[4].weight=14;ht[5].weight=23;ht[6].weight=3;ht[7].weight=11;huffmanCoding hc(ht,8);hc.Encode(8);for(int i=0;i<8;++i)cout<<hc.HC[i]<<endl;return 0;}
測試結果,即編碼結果為:
大家可能發現 與書上的結果不一樣,嚴的書是 左節點必須為葉子節點,並且強制附0,我認為這不正確。huffman的定義不是這樣的,如有異議可交流。