C++實(shí)現(xiàn)Huffman的編解碼
Huffman編碼主要是通過(guò)統(tǒng)計(jì)各元素出現(xiàn)的頻率,進(jìn)而生成編碼最終達(dá)到壓縮的目的。
這里是Huffman樹(shù)中節(jié)點(diǎn)的結(jié)構(gòu)。
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;
首先用一個(gè)名為freq的hashtable來(lái)記錄各個(gè)元素的頻率:
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樹(shù)的構(gòu)建過(guò)程如下代碼所示:
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為一種優(yōu)先隊(duì)列 } while(true)//構(gòu)建哈夫曼樹(shù) { 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); //合并節(jié)點(diǎn) 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函數(shù)如下:
void print_Code(Tree *proot, string st)//從根節(jié)點(diǎn)開(kāi)始打印,左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的編碼。
當(dāng)然,由于huffman的編碼都是0或1,因此需要進(jìn)行位的表示才能完成壓縮。注意,位需要以8個(gè)為一組進(jìn)行寫(xiě)入:
while(in>>a){ int x=atoi(a.c_str()); auto m=encoded.find(x); //encoded是另外一個(gè)哈希表用于記錄元素及它的編碼 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')//位寫(xiě)入,8個(gè)為一組 { buf = buf | ((ss[i++]-'0') << (7 - count)); count++; if (count == 8) { count = 0; fout << buf; buf = 0; } } if(count != 0) fout << buf;
至此就完成了Huffman的編碼以及壓縮,效果十分可觀:
當(dāng)對(duì)69.6M的txt文件(其中含有大約10000000個(gè)數(shù)據(jù))進(jìn)行壓縮時(shí),產(chǎn)生的encoded.bin文件僅為24.6MB:Ubuntu測(cè)試環(huán)境:
下面進(jìn)行Huffman解碼的解說(shuō):
Huffman解碼首先是根據(jù)編碼表進(jìn)行huffman樹(shù)的重建:
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();//將位轉(zhuǎn)為字符串 }
然后用Huffman樹(shù)根據(jù)左0右1進(jìn)行查找并輸出:
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樹(shù)的解碼:
總的來(lái)說(shuō),Huffman對(duì)于大數(shù)據(jù)的壓縮效果是很好的,運(yùn)行時(shí)間也很快,大概需要20s就可以完成對(duì)1000000個(gè)數(shù)據(jù)的編碼壓縮。
難點(diǎn)在于位的寫(xiě)入與讀取,花了相當(dāng)多的精力進(jìn)行操作。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- C++實(shí)現(xiàn)哈夫曼編碼
- C++實(shí)現(xiàn)哈夫曼樹(shù)編碼解碼
- C++實(shí)現(xiàn)哈夫曼樹(shù)算法
- 基于C++實(shí)現(xiàn)的哈夫曼編碼解碼操作示例
- C++數(shù)據(jù)結(jié)構(gòu)與算法之哈夫曼樹(shù)的實(shí)現(xiàn)方法
- C++ 哈夫曼樹(shù)對(duì)文件壓縮、加密實(shí)現(xiàn)代碼
- C++數(shù)據(jù)結(jié)構(gòu)之文件壓縮(哈夫曼樹(shù))實(shí)例詳解
- 解析C++哈夫曼樹(shù)編碼和譯碼的實(shí)現(xiàn)
- C++實(shí)現(xiàn)哈夫曼樹(shù)簡(jiǎn)單創(chuàng)建與遍歷的方法
相關(guān)文章
詳解C++中stoi/stol/stoll函數(shù)的用法
這篇文章主要為大家詳細(xì)介紹了C++中stoi、stol、stoll函數(shù)的具體用法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)校C++有一點(diǎn)的幫助,需要的可以參考一下2023-03-03C++生成dll和調(diào)用dll的方法實(shí)例
C++生成dll和調(diào)用dll的方法實(shí)例,需要的朋友可以參考一下2013-03-03C語(yǔ)言入門(mén)篇--變量[定義,初始化賦值,外部聲明]
本篇文章是c語(yǔ)言基礎(chǔ)篇,本文對(duì)初識(shí)c語(yǔ)言的變量、變量的定義、初始化與賦值、變量的分類(lèi)、含義、外部聲明做了簡(jiǎn)要的描述,幫助大家快速入門(mén)c語(yǔ)言的世界,更好的理解c語(yǔ)言2021-08-08C語(yǔ)言遞歸函數(shù)與漢諾塔問(wèn)題簡(jiǎn)明理解
遞歸(recursive)函數(shù)是“自己調(diào)用自己”的函數(shù),無(wú)論是采用直接或間接調(diào)用方式。間接遞歸意味著函數(shù)調(diào)用另一個(gè)函數(shù)(然后可能又調(diào)用第三個(gè)函數(shù)等),最后又調(diào)用第一個(gè)函數(shù)。因?yàn)楹瘮?shù)不可以一直不停地調(diào)用自己,所以遞歸函數(shù)一定具備結(jié)束條件2022-07-07C++ Qt開(kāi)發(fā)之ComboBox下拉組合框組件用法詳解
Qt 是一個(gè)跨平臺(tái)C++圖形界面開(kāi)發(fā)庫(kù),利用Qt可以快速開(kāi)發(fā)跨平臺(tái)窗體應(yīng)用程序,在Qt中,ComboBox(組合框)是一種常用的用戶界面控件,它提供了一個(gè)下拉列表,允許用戶從預(yù)定義的選項(xiàng)中選擇一個(gè),本文給大家介紹QComboBox類(lèi)的一些常用方法,需要的朋友可以參考下2023-12-12