C++實現Huffman的編解碼
阿新 • • 發佈:2020-04-28
Huffman編碼主要是通過統計各元素出現的頻率,進而生成編碼最終達到壓縮的目的。
這裡是Huffman樹中節點的結構。
typedef struct Tree { int freq;//頻率 int key;//鍵值 struct Tree *left,*right; Tree(int fr=0,int k=0,Tree *l=nullptr,Tree *r=nullptr): freq(fr),key(k),left(l),right(r){}; }Tree,*pTree;
首先用一個名為freq的hashtable來記錄各個元素的頻率:
void read(){ int a; std::ios::sync_with_stdio(false); while(cin>>a){ if(freq.find(a)==freq.end()) {freq[a]=1;} else {freq[a]++;} } }
Huffman樹的構建過程如下程式碼所示:
void huffman() { int i; string c; int fr; auto it = freq.begin(); while(it!=freq.end()){ Tree *pt= new Tree; pt->key = it->first; pt->freq = it->second; it++; th.Insert(pt);//此處的th為一種優先佇列 } while(true)//構建哈夫曼樹 { Tree *proot = new Tree; pTree pl,pr; pl = th.findMin(); th.Delete(0); if(th.isEmpty()){ th.Insert(pl); break; } pr = th.findMin(); th.Delete(0); //合併節點 proot->freq = pl->freq + pr->freq; std::ios::sync_with_stdio(false); proot->left = pl; proot->right = pr; th.Insert(proot); //合併後再插入 } string s; print_Code(th.findMin(),s); del(th.findMin()); }
其中print_Code和del函式如下:
void print_Code(Tree *proot,string st)//從根節點開始列印,左0右1 { if(proot == NULL) return ; if(proot->left) { st +='0'; } print_Code(proot->left,st); std::ios::sync_with_stdio(false); if(!proot->left && !proot->right) { cout<<proot->key<<" "; for(size_t i=0; i<st.length(); ++i){ cout<<st[i];ml+=st[i]; } cout<<endl;encoded[proot->key]=ml;ml=""; } st.pop_back(); if(proot->right) st+='1'; print_Code(proot->right,st); } void del(Tree *proot) { if(proot == nullptr) return ; del(proot->left); del(proot->right); delete proot; }
至此就完成了Huffman的編碼。
當然,由於huffman的編碼都是0或1,因此需要進行位的表示才能完成壓縮。注意,位需要以8個為一組進行寫入:
while(in>>a){ int x=atoi(a.c_str()); auto m=encoded.find(x); //encoded是另外一個雜湊表用於記錄元素及它的編碼 if(m==encoded.end()) continue; else { string t=m->second; ss+=t; } } unsigned char buf = 0; int count = 0; int i = 0; while(ss[i] != '\0')//位寫入,8個為一組 { buf = buf | ((ss[i++]-'0') << (7 - count)); count++; if (count == 8) { count = 0; fout << buf; buf = 0; } } if(count != 0) fout << buf;
至此就完成了Huffman的編碼以及壓縮,效果十分可觀:
當對69.6M的txt檔案(其中含有大約10000000個數據)進行壓縮時,產生的encoded.bin檔案僅為24.6MB:Ubuntu測試環境:
下面進行Huffman解碼的解說:
Huffman解碼首先是根據編碼表進行huffman樹的重建:
void decode(){ auto it=decoded.begin(); Tree *p=proot; while(it!=decoded.end()){ string s=it->first;int t=it->second;int i=0; while(i<s.length()){ if(s[i]=='0') { if(proot->left==nullptr) proot->left=new Tree(5); proot=proot->left; } else{ if(proot->right==nullptr) proot->right=new Tree(5); proot=proot->right; } i++; } proot->data=t; proot=p; it++; } }
然後讀取bin檔案的一系列位:
while (f.get(c)){ stringstream a; for (int i = 7; i > 0; i--) a<<((c >> i) & 1); a<<(c&1); m+=a.str();//將位轉為字串 }
然後用Huffman樹根據左0右1進行查詢並輸出:
int i=0;Tree *p=proot; while(i<m.length()){ if(m[i]=='0'&&proot->left!=nullptr) {proot=proot->left;i++;} else if(m[i]=='1'&&proot->right!=nullptr) {proot=proot->right;i++;} else {cout<<proot->data<<endl;proot=p;} }
至此就完成了Huffman樹的解碼:
總的來說,Huffman對於大資料的壓縮效果是很好的,執行時間也很快,大概需要20s就可以完成對1000000個數據的編碼壓縮。
難點在於位的寫入與讀取,花了相當多的精力進行操作。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。