Huffman(哈夫曼)樹編碼與解碼程式(全)
阿新 • • 發佈:2018-12-22
關於Huffman樹構建與編碼的原理,很多書上有介紹,我在這裡就只給出相應的程式,包括樹的構建,2種編碼方法,譯碼(這部分是我自己獨立寫的,肯定有不當之處,歡迎回帖指正)等,裡面註釋也很清晰,費了很大勁,希望對大家有幫助。
<span style="font-size:18px;"><span style="font-size:16px;">//[email protected] #include <iostream> using namespace std; int m, s1, s2; // m是總結點個數,s1,s2用於篩選出最小和第二小的兩個數 typedef struct{ unsigned int weight; unsigned int parent, lchild, rchild; }HTNode, *HuffmanTree; //動態分配陣列儲存哈夫曼樹 typedef char* HuffmanCode; //動態分配陣列儲存哈夫曼編碼表 //選出weight最小的兩個結點,s1儲存最小的,s2儲存第二小的 void SelectMin(HuffmanTree HT, int nNode) { int i, j; for(i = 1; i <= nNode; i++) if(!HT[i].parent) { s1 = i; break; } for(j = i+1; j <= nNode; j++) if(!HT[j].parent) { s2 = j; break; } for(i = 1; i <= nNode; i++) if((HT[i].weight < HT[s1].weight) && (!HT[i].parent) && (s2 != i)) s1 = i; for(j = 1; j <= nNode; j++) if((HT[j].weight < HT[s2].weight) && (!HT[j].parent) && (s1 != j)) s2 = j; // 以上只篩選出最小的兩個,這裡保證s1的weight比s2的小 if(HT[s1].weight > HT[s2].weight) { int tmp = s1; s1 = s2; s2 = tmp; } } // w[]存放nNode個字元的權值(均大於0),構造哈夫曼樹HT, // 並求出nNode個字元的哈夫曼編碼HC void HuffmanCoding(HuffmanTree &HT, HuffmanCode *&HC, int *w, int nNode) { int i, j; char *hfcode; int p; int cdlen; if(nNode < 1) return; m = 2*nNode-1; //哈夫曼樹的結點數,定理公式 /////////////////////////////以下是求Huffman樹的初始化///////////////////////////// HT = (HTNode*) malloc ((m+1) *sizeof(HTNode)); //0號單元未用 for(i = 1; i <= nNode; i++) //初始化 { HT[i].weight = w[i-1]; HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0; } for(i = nNode+1; i <= m; i++) { HT[i].weight = 0; HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0; } /////////////////////////////以下是Huffman樹的構建///////////////////////////// for(i = nNode+1; i <= m; i++) { // 建立哈夫曼樹 // 在HT[1..i-1]中選擇parent為0且weight最小的兩個節點 // 其序號分別是s1和s2,並且小的是左孩子 SelectMin(HT, i-1); HT[s1].parent = i; HT[s2].parent = i; //cout << "S1 && S2: " << HT[s1].weight << " " << HT[s2].weight << endl; HT[i].lchild = s1; HT[i].rchild = s2; HT[i].weight = HT[s1].weight + HT[s2].weight; } /////////////////////////////以下是求Huffman樹的編碼///////////////////////////// ////////////////////////方法一/////////////////////////// /*該方法是從每個葉結點開始上溯,以從後向前的方式生成huffman編碼*/ hfcode = (char *) malloc ( (nNode + 1) * sizeof( char ) ); hfcode[nNode] = '\0'; //編碼以‘\0’結尾 int start; int c; //c:當前處理節點,p是c的父結點 for(int i=1; i<=nNode; i++) { start = nNode; for(c=i, p=HT[c].parent; p!=0; c=p,p=HT[p].parent) { if(c==HT[p].lchild) hfcode[--start]='0'; else if(c==HT[p].rchild) hfcode[--start]='1'; } //申請足夠存放該節點編碼就行,不浪費; HC[i] = (char *) malloc ((nNode-start+1) * sizeof(char)); strcpy(HC[i], &hfcode[start]); } free(hfcode); ////////////////////////方法二/////////////////////////// // 該方法從根出發,遞迴遍歷哈夫曼樹,求得編碼。標記0,1,2含義如下: //0:搜到一個滿足條件的新結點 //1:當前正在搜其兒子結點的結點,還沒有回溯回來 //2:不滿足條件或者兒子結點已全部搜完,已經回溯回來,則回溯到其父結點 //注意:當流程走到一個結點後,其標記立即變為下一狀態, //即:0->1->2->0(最後一步2->0不是必需的),但執行條件仍然是當前狀態 /*hfcode = (char *) malloc (nNode * sizeof(char)); //分配求編碼的工作空間 p = m; cdlen = 0; for(i = 1; i <= m; i++) HT[i].weight = 0; //遍歷哈夫曼樹時用作結點狀態的標誌 while(p) //退出條件:p = 結點m的parent,即為0 { if(HT[p].weight == 0) //向左走 { HT[p].weight = 1; if(HT[p].lchild != 0) { p = HT[p].lchild; hfcode[cdlen++] = '0'; } //else if(HT[p].rchild == 0) //左右孩子都為0,葉結點 //{ // HC[p] = (char *) malloc ((cdlen+1) * sizeof(char)); // hfcode[cdlen] = '\0'; //保證後面的不會被複制 // strcpy(HC[p], hfcode); //複製編碼 //} } else if(HT[p].weight == 1) //向右走 { HT[p].weight = 2; if(HT[p].rchild != 0) { p = HT[p].rchild; hfcode[cdlen++] = '1'; } //該分支放在這裡似乎更合理一點,放上面被註釋掉的地方也可以 else if(HT[p].rchild == 0) //左右孩子都為0,葉結點 { HC[p] = (char *) malloc ((cdlen+1) * sizeof(char)); hfcode[cdlen] = '\0'; //保證後面的不會被複制 strcpy(HC[p], hfcode); //複製編碼 } } else //HT[p].weight == 2 退回到父結點,編碼長度減一 { HT[p].weight = 0; p = HT[p].parent; --cdlen; } }*/ } /*Huffman解碼函式 *HT:Huffman樹,w[]:權值陣列(從下標0開始),code[]:要解碼的串 */ void HuffmanDecode(HuffmanTree HT, int w[], char code[]) { char *ch = code; int i; while( *ch != '\0' ){ //解碼一個結點每次都從樹根m開始 for(i=m; HT[i].lchild !=0 && HT[i].rchild != 0; ){ if( *ch == '0' ) i = HT[i].lchild; else if( *ch == '1' ) i = HT[i].rchild; ++ch; } cout<<w[i-1]<<" "; } } int main() { HuffmanTree HT = NULL; // 哈夫曼樹 HuffmanCode *HC; // 儲存哈夫曼編碼 int *w, nNode, i; // w記錄權值 char CodeStr[20]= {0}; //存放編碼後的串 cout<<"輸入結點數(>=2): "<<endl; cin>>nNode; HC = (HuffmanCode *) malloc (nNode* sizeof(HuffmanCode)); w = (int *) malloc (nNode * sizeof(int)); cout<<"輸入 "<<nNode<<" 個結點的權值\n"; for(i = 0; i < nNode; i++) scanf("%d", &w[i]); HuffmanCoding(HT, HC, w, nNode); cout<<"\n各結點的哈夫曼編碼:"<<endl; for(i = 1; i <= nNode; i++){ printf("%2d(%d):%s\n", i, w[i-1], HC[i]); strcat(CodeStr, HC[nNode-i+1]); //簡單生成一個huffman碼串 } cout<<"對哈夫曼編碼\""<<CodeStr<<"\"的解碼如下:"<<endl; HuffmanDecode(HT, w, CodeStr); return 0; }</span></span>