昨天看了收藏的陈皓童鞋的《Huffman 编码压缩算法》,想想自己以前学的时候没有亲手实现过,于是又参照了维基百科上面的Huffman编码文章实现了一下,比维基上面实现的要详细。
主函数的运行流程包含了从读文件到统计频率到产生每个字符编码到编码到解码的全过程。程序运行后,依次在控制台打印每个字符的编码、编码后的全文(由input.txt文件读入)、解密后的原文。
除Huffman算法本身以外,本篇文章的代码涉及到C++里面的流与IO、STL里的容器和算法、树结构和递归回溯,这样看来也算是一个不错的综合性小程序。代码如下:
#include<iostream> #include<fstream> #include<iomanip> #include<deque> #include<map> #include<algorithm> using namespace std; struct node { char ch; unsigned frequency; node* lchild; node* rchild; }; deque<node*> forest; node* ptr; map<char,string> dict; //保存每个字符和对应的01串编码 bool compare(node* a,node* b) { return a->frequency < b->frequency; } node* makeTree(char* buffer,int length) { deque<node*>::iterator it; for(int i=0;i<length;i++)//统计字符频率 { int flag=0; for (it=forest.begin();it!=forest.end();++it) { if((*it)->ch==buffer[i]) { ((*it)->frequency)++; flag=1; break; } } if(flag==0) { ptr=new node; ptr->ch=buffer[i]; ptr->frequency=1; ptr->lchild=NULL; ptr->rchild=NULL; forest.push_back(ptr); } } int size=forest.size(); for (int i=0;i<size-1;i++)//从森林建树 { sort(forest.begin(),forest.end(),compare);//TODO:可以用优先队列改写一遍 ptr=new node; ptr->frequency=forest[0]->frequency+forest[1]->frequency; ptr->lchild=forest[0]; ptr->rchild=forest[1]; forest.pop_front(); forest.pop_front(); forest.push_back(ptr); } ptr=forest.front();//ptr指向根节点 return ptr; } int counter=0; char buffer[30]={'\0'}; void generateCharCode(node* root) { if(root->lchild==NULL && root->rchild==NULL) { buffer[counter]='\0'; cout<<setw(10)<<buffer<<":"<<root->ch<<endl;//打印编码表 dict[root->ch]=buffer; return ; } if(root->lchild!=NULL) { buffer[counter++]='0'; generateCharCode(root->lchild); counter--; } if(root->rchild!=NULL) { buffer[counter++]='1'; generateCharCode(root->rchild); counter--; } } string Encode(char* str,int len) { string newStr=""; for(int i=0;i<len-5;i++)//计算出来的len总比有效长度大5,所以在这里减去,至于为什么还没弄明白 { newStr.append(dict[str[i]]); } return newStr; } void Decode(node* root,string str,int& index) { if(root->lchild==NULL && root->rchild==NULL) { cout<<root->ch; return; } if('0'==str[index] && root->lchild!=NULL) { index++; Decode(root->lchild,str,index); } else if('1'==str[index] && root->rchild!=NULL) { index++; Decode(root->rchild,str,index); } } string Decode2(string str) { int index=0; string text=""; map<char,string>::iterator it; while(index<str.size()) { for(it=dict.begin();it!=dict.end();++it) { if(str.substr(index,(it->second).size()) == it->second) { text+=it->first;//or: text+=string(1, it->first); index+=(it->second).size(); break; } } } return text; } int main(int argc, char *argv[]) { //TODO:读文件可以抽象成函数。主函数就这里还没抽象。 fstream filestr("input.txt", fstream::in | fstream::out); filestr.seekg(0,filestr.end); int length = filestr.tellg(); filestr.seekg(0,filestr.beg); char* buffer = new char[length];//没有delete filestr.read(buffer,length); generateCharCode(makeTree(buffer,length)); string encodedText = Encode(buffer,length); cout<<encodedText<<endl; int indx=0; while(indx<encodedText.size()) { Decode(ptr,encodedText,indx); } //上面4行可以用:Decode2(encodedText);代替 return 0; }