C++实现Huffman的编解码

 更新时间:2020年04月28日 09:13:45   作者:漆黑烈焰使  
这篇文章主要为大家详细介绍了C++实现Huffman的编解码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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个数据的编码压缩。

难点在于位的写入与读取,花了相当多的精力进行操作。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 详解C++中stoi/stol/stoll函数的用法

    详解C++中stoi/stol/stoll函数的用法

    这篇文章主要为大家详细介绍了C++中stoi、stol、stoll函数的具体用法,文中的示例代码讲解详细,对我们学校C++有一点的帮助,需要的可以参考一下
    2023-03-03
  • 基于C中含有if的宏定义详解

    基于C中含有if的宏定义详解

    本篇文章是对C中含有if的宏定义进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++生成dll和调用dll的方法实例

    C++生成dll和调用dll的方法实例

    C++生成dll和调用dll的方法实例,需要的朋友可以参考一下
    2013-03-03
  • C语言中的指针以及二级指针代码详解

    C语言中的指针以及二级指针代码详解

    这篇文章主要介绍了C语言中的指针以及二级指针代码详解,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • c++中的const_cast用法大全

    c++中的const_cast用法大全

    const_cast转换符是用来移除变量的const或volatile限定符。对于后者,我不是太清楚,因为它涉及到了多线程的设计,今天重点给大家介绍c++中的const_cast用法大全,需要的朋友参考下吧
    2021-07-07
  • C语言实现2048小游戏

    C语言实现2048小游戏

    这篇文章主要为大家详细介绍了C语言实现2048小游戏,注释清晰,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • C语言入门篇--变量[定义,初始化赋值,外部声明]

    C语言入门篇--变量[定义,初始化赋值,外部声明]

    本篇文章是c语言基础篇,本文对初识c语言的变量、变量的定义、初始化与赋值、变量的分类、含义、外部声明做了简要的描述,帮助大家快速入门c语言的世界,更好的理解c语言
    2021-08-08
  • C语言递归函数与汉诺塔问题简明理解

    C语言递归函数与汉诺塔问题简明理解

    递归(recursive)函数是“自己调用自己”的函数,无论是采用直接或间接调用方式。间接递归意味着函数调用另一个函数(然后可能又调用第三个函数等),最后又调用第一个函数。因为函数不可以一直不停地调用自己,所以递归函数一定具备结束条件
    2022-07-07
  • C C++ 算法实例大全

    C C++ 算法实例大全

    这篇文章主要介绍了C C++ 算法实例大全,里面大量的实例介绍,学习c语言的朋友可以收藏
    2016-12-12
  • C++ Qt开发之ComboBox下拉组合框组件用法详解

    C++ Qt开发之ComboBox下拉组合框组件用法详解

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中,ComboBox(组合框)是一种常用的用户界面控件,它提供了一个下拉列表,允许用户从预定义的选项中选择一个,本文给大家介绍QComboBox类的一些常用方法,需要的朋友可以参考下
    2023-12-12

最新评论