哈夫曼树编码与译码(完整C/C++实现代码)
哈夫曼編碼的設(shè)計(jì)與應(yīng)用
問題需求分析
用哈夫曼編碼(Huffman Coding),又稱霍夫曼編碼,是一種編碼方式,哈夫曼編碼是可變字長(zhǎng)編碼(VLC)的一種。Huffman于1952年提出一種編碼方法,該方法完全依據(jù)字符出現(xiàn)概率來構(gòu)造異字頭的平均長(zhǎng)度最短的碼字,有時(shí)稱之為最佳編碼,一般就叫做Huffman編碼(有時(shí)也稱為霍夫曼編碼)。
霍夫曼樹又稱最優(yōu)二叉樹,是一種帶權(quán)路徑長(zhǎng)度最短的二叉樹。所謂樹的帶權(quán)路徑長(zhǎng)度,就是樹中所有的葉結(jié)點(diǎn)的權(quán)值乘上其到根結(jié)點(diǎn)的路徑長(zhǎng)度(若根結(jié)點(diǎn)為0層,葉結(jié)點(diǎn)到根結(jié)點(diǎn)的路徑長(zhǎng)度為葉結(jié)點(diǎn)的層數(shù))。樹的路徑長(zhǎng)度是從樹根到每一結(jié)點(diǎn)的路徑長(zhǎng)度之和,記為WPL=(W1L1+W2L2+W3L3+…+WnLn),N個(gè)權(quán)值Wi(i=1,2,…n)構(gòu)成一棵有N個(gè)葉結(jié)點(diǎn)的二叉樹,相應(yīng)的葉結(jié)點(diǎn)的路徑長(zhǎng)度為L(zhǎng)i(i=1,2,…n)。
數(shù)據(jù)結(jié)構(gòu)的定義
哈夫曼樹結(jié)構(gòu)體包含如下內(nèi)容:節(jié)點(diǎn)權(quán)重,當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn),左右子樹,節(jié)點(diǎn)信息。
哈夫曼編碼結(jié)構(gòu)體包含如下內(nèi)容:存編碼的數(shù)組,編碼數(shù)組的開始標(biāo)志。
功能詳細(xì)設(shè)計(jì)
構(gòu)建哈夫曼樹:使用一維數(shù)組,每個(gè)節(jié)點(diǎn)存儲(chǔ)權(quán)重,父節(jié)點(diǎn),左子樹,右子樹,以及數(shù)值的信息。遍歷整個(gè)數(shù)組,根據(jù)每個(gè)節(jié)點(diǎn)的權(quán)重去找到最小以及第二小的節(jié)點(diǎn),然后對(duì)各自的父節(jié)點(diǎn),左右子樹賦值,建立兩個(gè)節(jié)點(diǎn)的聯(lián)系將他們的聯(lián)系填入該結(jié)構(gòu)體數(shù)組中。
哈夫曼樹如下圖:
a權(quán)重45; b權(quán)重15; c權(quán)重12; d權(quán)重16; e權(quán)重9; f權(quán)重5
哈夫曼樹結(jié)構(gòu)體數(shù)組如下圖(使用a, b, 58舉例):
根據(jù)哈夫曼樹建立哈夫曼編碼:從樹的根節(jié)點(diǎn)開始,根據(jù)每個(gè)葉子節(jié)點(diǎn)與父節(jié)點(diǎn)之間的聯(lián)系,使用哈夫曼編碼結(jié)構(gòu)體中的編碼數(shù)組儲(chǔ)存樹種每個(gè)葉子節(jié)點(diǎn)的編碼, 往左邊遍歷是0, 往右邊遍歷是1, 舉例: a, 由建立好的哈夫曼樹可得, 當(dāng)遍歷查找到a的時(shí)候, a到root的路徑是: 100->86->58->a, 所以可得編碼就是000;
根據(jù)哈夫曼編碼將指定的編碼轉(zhuǎn)化為字符串:讀取到哈夫曼編碼后,當(dāng)編碼為0表示節(jié)點(diǎn)是左子樹,當(dāng)編碼為1的時(shí)候表示右子樹,根據(jù)已經(jīng)建立好的哈夫曼樹,利用順序遍歷的方式,根據(jù)左0右1,尋找哈夫曼樹中的葉子節(jié)點(diǎn),當(dāng)某一節(jié)點(diǎn)在哈夫曼樹中左右子樹都為空的時(shí)候,表示該節(jié)點(diǎn)即使葉子節(jié)點(diǎn),然后輸出對(duì)應(yīng)葉子節(jié)點(diǎn)在哈夫曼編碼數(shù)組中的位置, 其原理與編碼的方式恰恰相符, 而是根據(jù)01數(shù)字查找對(duì)應(yīng)的葉子節(jié)點(diǎn).
函數(shù)調(diào)用圖:
編寫代碼體會(huì)
遇到的問題是數(shù)組下標(biāo)沒有弄好,導(dǎo)致建立哈夫曼樹時(shí)出現(xiàn)各種錯(cuò)誤, 進(jìn)行編碼過程沒有將lchild與rchild的”變化寫在一起”導(dǎo)致出現(xiàn)錯(cuò)誤,其實(shí)最大的問題是:沒有將代碼的邏輯思路考慮清楚,還有邊界條件值,最蠢的是沒有打一下草稿,寫一下偽代碼,把程序的思想理解,導(dǎo)致編程的過程中出現(xiàn)各種問題,以后寫代碼之前還是把邏輯理清楚再動(dòng)手寫代碼,最后再將代碼寫到電腦上測(cè)試。
完整代碼
#include<iostream> #include<string> #include<fstream> using namespace std; struct Htnode{int weight,parent,lchild,rchild;char c; }; struct Htcode{int bit[25],start; }; int type(int a[]){string s;int sum = 0; cout<<"輸入需要編碼的字符串"<<endl;cin>>s;for(int i = 0; i < s.size(); i++){a[s[i]-'a']++;}cout<<"出現(xiàn)的字母種類以及頻率:"<<endl; for(int i = 0; i < 26; i++){if(a[i] != 0){char c = char(i+'a');cout<<"i:"<<i<<" c:"<<c<<":"<<a[i]/(s.size()*1.0)<<endl;sum++; }}return sum; } //通過數(shù)組的方式構(gòu)建哈夫曼樹 void HufmanTree(Htnode h[],int n, int a[]){int i,j,max1,max2,x1,x2;for(i=0;i<2*n;i++){h[i].weight=0;h[i].parent=-1;h[i].lchild=-1;h[i].rchild=-1;h[i].c='\0';}for(i=0;i<26;i++){if(a[i] != 0){h[i].c = i + 'a';h[i].weight = a[i]; }}for(i=0;i<n-1;i++){max1=1000,max2=1000;x1=-1,x2=-1; for(j=0;j<n+i;j++){if(h[j].weight<max1 && h[j].parent==-1){max2=max1;x2=x1;max1=h[j].weight;x1=j;}else if(h[j].weight<max2 && h[j].parent==-1){max2=h[j].weight;x2=j;}}//根據(jù)每個(gè)節(jié)點(diǎn)信息, 將其信息存儲(chǔ)到節(jié)點(diǎn)數(shù)組中h[x1].parent=n+i; h[x2].parent=n+i; h[n+i].weight=h[x1].weight+h[x2].weight;h[n+i].lchild=x1;h[n+i].rchild=x2;}for (i=0;i<2*n-1;i++){cout<<h[i].weight<<" "<<h[i].parent<<" "<<h[i].lchild<<" "<<h[i].rchild<<" "<<h[i].c<<endl; } } //哈夫曼編碼 //根據(jù)葉子節(jié)點(diǎn)的位置, 將其path路徑01數(shù)字填充到編碼數(shù)組中 void HuffmandeCode(Htnode h[], int n, int a[], Htcode hcode[]){HufmanTree(h, n, a);ofstream out;out.open("HuffmandeCode.txt", ios::out);int i,j;for(i=0;i<n;i++){j=0;int parent=h[i].parent;//記錄當(dāng)前節(jié)點(diǎn)的父親 int c=i;while(c!=-1){//parent造成根節(jié)點(diǎn)不會(huì)被訪問 hcode[i].bit[j++]=h[parent].lchild==c?0:1;//從葉子節(jié)點(diǎn)到根節(jié)點(diǎn), 應(yīng)該使用棧結(jié)構(gòu) c=parent;parent=h[parent].parent;}hcode[i].start=j-1;}for(i=0;i<n;i++){cout<<h[i].c<<":";for(j=hcode[i].start-1;j>=0;j--){cout<<hcode[i].bit[j];out<<hcode[i].bit[j];}cout<<endl;}out.close(); } string load(){ifstream in("HuffmandeCode.txt");string str;char buffer[256];if(!in.is_open()){cout<<"加載文件錯(cuò)誤"<<endl; return NULL;} cout << "載入編碼文件" << endl;in.getline(buffer, 100, ' ');return string(buffer); }//哈夫曼譯碼 void HuffmanenCode(string s,int n,Htnode h[]){int i=0,j=0,lchild=2*n-2,rchild=2*n-2;while(s[i]!='\0'){if(s[i]=='0'){//出現(xiàn)的問題是,最初將lchild,rchild分開計(jì)算,導(dǎo)致在左右子樹間相互變化出現(xiàn)//lchild,rchild不同同時(shí)表示同一個(gè)節(jié)點(diǎn),最后想到lchild=rchild就能解決問題lchild=h[lchild].lchild;rchild=j=lchild;}if(s[i]=='1'){rchild=h[rchild].rchild;lchild=j=rchild;}if(h[lchild].lchild==-1 && h[rchild].rchild==-1){cout<<h[j].c;lchild=rchild=2*n-2;j=0;}i++;} } int main(){Htnode h[30];Htcode hcode[10];int a[26]={0};string s;int n = type(a);cout<<"n:"<<n<<endl;HuffmandeCode(h, n, a, hcode);HuffmanenCode(load(),n,h);return 0; }上面有錯(cuò), 還請(qǐng)指出, 如果認(rèn)為我寫的還不錯(cuò), 還請(qǐng)點(diǎn)個(gè)贊, 多多支持一下, O(∩_∩)O~~
總結(jié)
以上是生活随笔為你收集整理的哈夫曼树编码与译码(完整C/C++实现代码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机java软件_浅谈软件开发就业前景
- 下一篇: c++ string 删除字符_字符串操