C++實(shí)現(xiàn)Huffman的編解碼
Huffman編碼主要是通過統(tǒng)計各元素出現(xiàn)的頻率,進(jìn)而生成編碼最終達(dá)到壓縮的目的。
這里是Huffman樹中節(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;
首先用一個名為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樹的構(gòu)建過程如下代碼所示:
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)先隊列
}
while(true)//構(gòu)建哈夫曼樹
{
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)開始打印,左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個為一組進(jìn)行寫入:
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的編碼以及壓縮,效果十分可觀:
當(dāng)對69.6M的txt文件(其中含有大約10000000個數(shù)據(jù))進(jìn)行壓縮時,產(chǎn)生的encoded.bin文件僅為24.6MB:Ubuntu測試環(huán)境:

下面進(jìn)行Huffman解碼的解說:
Huffman解碼首先是根據(jù)編碼表進(jìn)行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();//將位轉(zhuǎn)為字符串
}
然后用Huffman樹根據(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樹的解碼:

總的來說,Huffman對于大數(shù)據(jù)的壓縮效果是很好的,運(yùn)行時間也很快,大概需要20s就可以完成對1000000個數(shù)據(jù)的編碼壓縮。
難點(diǎn)在于位的寫入與讀取,花了相當(dāng)多的精力進(jìn)行操作。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解C++中stoi/stol/stoll函數(shù)的用法
這篇文章主要為大家詳細(xì)介紹了C++中stoi、stol、stoll函數(shù)的具體用法,文中的示例代碼講解詳細(xì),對我們學(xué)校C++有一點(diǎn)的幫助,需要的可以參考一下2023-03-03
C++生成dll和調(diào)用dll的方法實(shí)例
C++生成dll和調(diào)用dll的方法實(shí)例,需要的朋友可以參考一下2013-03-03
C++ Qt開發(fā)之ComboBox下拉組合框組件用法詳解
Qt 是一個跨平臺C++圖形界面開發(fā)庫,利用Qt可以快速開發(fā)跨平臺窗體應(yīng)用程序,在Qt中,ComboBox(組合框)是一種常用的用戶界面控件,它提供了一個下拉列表,允許用戶從預(yù)定義的選項(xiàng)中選擇一個,本文給大家介紹QComboBox類的一些常用方法,需要的朋友可以參考下2023-12-12

