Huffman编码与解码 (Huffman编码、二叉树)

Huffman编码与解码

[问题描述]

对一篇不少于2000字符的英文文章(source.txt),统计各字符出现的次数,实现Huffman编码(code.dat),以及对编码结果的解码(recode.txt)。

[基本要求]

(1) 输出每个字符出现的次数和编码,并存储文件(Huffman.txt)。

(2) 在Huffman编码后,英文文章编码结果保存到文件中(code.dat),编码结果必须是二进制形式,即0 1的信息用比特位表示,不能用字符’0’和’1’表示。

(3) 实现解码功能。

数据结构

//二叉树
typedef struct
BiTNode
{
    int data;//权值
    int word=300;//字母
    int HuffmanCode[100];
    struct BiTNode *lchild , *rchild;
}BiTNode ,
*BiTree;

算法设计思想

1.建立一个zm数组,从文件中一个个读取文章数据,利用哈希表的思想,根据所得字符的ASCII值在数组里分配地址,并对其值加一,以此统计字符的出现次数。

2.建立一个值为树结点的数组,存储文章的字符,每次选出ASCII值最小的两个字符作为新结点的左右子树,将新节点的权值置为左、右子树上根结点的权,即为ASCII值的和,再将刚刚所选的字符从数组里删除。重复上述步骤,直到构建出一个二叉树为止。

3.设置数组存储编码,利用中序遍历依次递归遍历哈夫曼树,对哈夫曼树中存储的字符调用编码函数进行编码,编码函数也用递归实现,向左走为0,向右为1。

4.对树中结点编码完毕后,从文章中依次读取字符,在树中查找相应的字符并存储编码到文件中;

5.最后再从文件中读取哈弗曼编码,在树中查找字符,0便进入左子树,1便进入右子树,找到后直接输出。

算法时间复杂度

BiTree CreateHuffman( int a[],BiTree &T ) //创建哈夫曼树

时间复杂度:O(n*logn)

void Creat_HCode (BiTree T , BiTree B ) //利用递归依次创建编码

时间复杂度:O(n*logn)

void bianma_T1 (BiTree T, int b[], int ch, int n ) //创建字符ch的编码

时间复杂度:O(n*logn)

全部代码如下:

#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include<fstream>
#include <math.h>
#include<string.h>
#include<iomanip>
using namespace std;
#define max 257
int TDepth;
int zm[max]={0};//存储每个字母的出现次数
int word[2000]={0};//存储字符
int num=0;

typedef struct BiTNode
{
    int data;//权值
    int word=300;//字母
    int HuffmanCode[100];
    struct BiTNode *lchild , *rchild;
}BiTNode , *BiTree;
//摧毁树
void DestroyBiTree ( BiTree &T )
{
   if ( T!= NULL )
   {
       DestroyBiTree ( T->lchild );
       DestroyBiTree ( T->rchild );
       free ( T );
       T = NULL;
   }
}

void fuzhi( BiTree &T, int b[] ,int n  )
{
    int i;
    b[0] = n;
    for ( i=0; i<=n; i++ )
    {
        T->HuffmanCode[i] = b[i];
    }
}
//创建字符ch的编码
void bianma_T1 ( BiTree T, int b[], int ch, int n )
{
    int i;
    if ( T )
    {
        if ( ch == T->word )//复制编码
        {
            b[0] = n;//存储编码长度
            for( i=0; i<=n; i++ )
            {
                T->HuffmanCode[i] = b[i];
            }
        }
        else
        {
            b[n]=0;
            bianma_T1 ( T->lchild , b ,  ch , n+1 );
            b[n]=1;
            bianma_T1 ( T->rchild , b , ch , n+1  );
        }
    }
}
//根据输入的字符输出编码
void bianma_T ( BiTree T , int b[], int ch , int n  )
{
    int i;
    if( T )
    {
        if( ch == T->data )
        {
             b[0] = n;
             for ( i=1; i<=n; i++ )
                cout<< b[i];
             cout<< endl;
        }
        else
        {
            b[n]=0;
            bianma_T1 ( T->lchild , b ,  ch , n+1 );
            b[n]=1;
            bianma_T1 ( T->rchild , b , ch , n+1  );
        }
    }
}
//利用递归依次创建编码
void Creat_HCode ( BiTree T ,  BiTree B )
{
    if ( T == NULL )
    {
        cout<<"此数为空"<<endl;
        return;
    }
    else
    {
      if ( T->word!=300 )//若此节点为文章内的字符,则对它进行编码
      {
          int ch = T->word;
          int b[100];
          bianma_T1 ( B , b , ch , 1);
      }
      Creat_HCode ( T->lchild , B );
      Creat_HCode ( T->rchild , B);
    }
}
//输出二叉树的信息
void Show_T ( BiTree T )
{
   int i;
   if ( T == NULL )
   {
       cout<<"此数为空"<<endl;
       return;
   }
   else
   {
       Show_T ( T->lchild );
       if ( T->word!=300 )//若此节点为文章内的字符,则向Huffman.txt存储结点信息
       {
          ofstream read_out;
          read_out.open( "Huffman.txt", ios::app );
          if( !read_out )
          {
            cout<< "文件打开失败!" <<endl;
            return;
          }
          printf( "字符:%c  ", T->word );
          printf( "ASCII值:%03d  ", T->word );//格式化输出,右对齐,左补零
          printf( "出现次数:%-3d  ", T->data );//格式化输出,左对齐,
          printf( "编码:");
          for( i=1 ; i < T->HuffmanCode[0]; i++)
          printf( "%d", T->HuffmanCode[i] );
          cout<<endl;
          char ch;
          ch = T->word;//将整型转化为字符型
          read_out<< "字符:"<< ch <<" 出现次数:"<< T->data <<" 编码:";
          for( i=1; i < T->HuffmanCode[0]; i++ )
          read_out<< T->HuffmanCode[i];
          read_out<<endl;
          read_out.close();
          read_out.clear();
       }
       Show_T( T->rchild);
    }
}
//返回哈夫曼树的深度
int BTreeDepth ( BiTree &T )
{
    int ldepth , rdepth;
    if ( T == NULL )
    {
         cout<<"此数为空"<<endl;
         return 0;
    }
    else
    {
        ldepth = BTreeDepth ( T -> lchild );
        rdepth = BTreeDepth ( T -> rchild );
    }
    if ( ldepth > rdepth )
        return ( ldepth + 1 );
    else return ( rdepth + 1 );
}
//创建哈夫曼树
BiTree CreateHuffman( int a[],BiTree &T )
{
    int i=0, j,k;
    BiTree b , q;
    BiTree keep[max];
    for ( k=0, j=0; j<max; j++ ) //初始化keep指针数组,使每个指针元素指向a数组中对应的元素结点
    {
        if ( a[j]!=0 )
        {
            b = (BiTNode *) malloc ( sizeof(BiTNode) );
            b->data = a[j];
            b->word = j;
            b->lchild = b->rchild = NULL;
            keep[k] = b;
            k++;
        }
    }
    //建立哈夫曼树
    for ( i=1; i<k; i++ )
    {
        int small = -1, big;
        //让small初始指向森林中第一棵树,big指向第二棵
        for ( j=0; j<k; j++ )
        {
            if ( keep[j]!= NULL && small==-1 )
            {
                small = j;
                continue;
            }
            if ( keep[j]!= NULL )
            {
                big = j;
                break;
            }
        }
        //从当前森林中求出最小权值树和次最小
        for ( j=big; j<k; j++)
        {
            if ( keep[j]!= NULL)
            {
                if ( keep[j]->data < keep[small]->data )
                {
                    big = small ;
                    small  = j;
                }
                else if ( keep[j]->data < keep[big]->data )
                    big = j;
            }
        }
        //由最小权值树和次最小权值树建立一棵新树,q指向树根结点
        q = (BiTNode *) malloc ( sizeof(BiTNode) );
        q->data = keep[small]->data + keep[big]->data;
        q->word = 300;
        q->lchild = keep[small];
        q->rchild = keep[big];
        keep[small] = q;//将指向新树的指针赋给b指针数组中small位置
        keep[big] = NULL;
    }
    if( b != NULL)
    {
        free(b);
        b = NULL;
    }
    T = q;
}
//初始化zm和word数组
void Get_zm ( BiTree T )
{
    int i=0;
    char ch;
    int y;
    ifstream read_in;
    read_in.open ( "source.txt" );
    if ( !read_in )
    {
        cout<< "文件读取失败!" <<endl;
        return;
    }
    while( !read_in.eof() )
    {
        if ( read_in.fail() )
            break;
        read_in.get( ch );
        y = (int)ch;//y为文章中字符ch的ASCII值
        zm[y]++;//利用哈希表的思想,统计每个字符出现的次数
        word[i] = y;//依次存储文章中每个字符
        i++;
    }
    num = i-2;//存储文章中字符数量
    read_in.close();
    read_in.clear();
}
//返回e的值
void Value1 ( BiTree &T , int y , int H[] , int &flag)
{
  if ( T != NULL&&flag==0 )
  {
     if ( T->word==y&&flag==0  )
     {
        H[0] = T->HuffmanCode[0];
        flag = 1;
        for ( int i=1; i<T->HuffmanCode[0] ; i++ )
        {
            H[i] = T->HuffmanCode[i] ;
        }
     }
     Value1 ( T->lchild , y ,H, flag  );
     Value1 ( T->rchild , y , H, flag );
  }
  return ;
}
//将文章内容编码并保存到code.dat
void save_dat( BiTree T , int flag )
{
    cout<< "*****************************************************" << endl;
    fstream file( "code.dat", ios::out|ios::binary );
    int i=0, j =0,k=0, y;
    int H[TDepth];
    for ( i=0; i<=num&&word[i]!=0; i++)
    {
        y = word[i];
        Value1 ( T, y, H, k );
        k = 0;
        file.write( (char*)H, sizeof(H) );
        if ( flag==1 )
        {
            for( j=1; j< H[0] ; j++ )
            cout<< H[j];
            cout<<endl;
        }
    }
    file.close();
    file.clear();
    cout<<"******文章编码结束******"<<endl;
}
//根据编码查找匹配的字符
int jiema ( BiTree T, int H[] )
{
    int i=0;
    for( i=1; i<H[0]; i++ )
    {
        if( H[i]==0 )
        T = T->lchild;
        else if ( H[i]==1 )
        T = T->rchild;
    }
    return T->word;
}
//对文章进行解码
void read_dat ( BiTree T )
{
    int j;
    fstream file( "code.dat", ios::in );
    if( file.fail() )
    {
        cout<<"打开code.dat出错"<<endl;
        exit(0);
    }
    int H[TDepth];
    while( !file.eof() )
    {
        file.read( (char*)H, sizeof(H) );
        j = jiema ( T ,  H );//j存储编码H所对应的字符
        printf( "%c", j );
    }
}

int main()
{
    int i=0;
    int ch;
    BiTree T;
    Get_zm ( T );//统计字符
    CreateHuffman( zm , T );//创建哈夫曼函数
    cout<< "********************文章字符信息如下*****************" << endl;
    Creat_HCode ( T , T);//编码
    Show_T ( T );
    TDepth = BTreeDepth( T );
    cout<< "是否输出文章的编码(1--是,0--否):";
    cin >> i;
    save_dat ( T , i);//将文章内容编码并保存
    cout<< "按任意键输出文章解码结果:";
    cin >> ch;
    read_dat ( T );//对文章解码
    DestroyBiTree ( T );
    return 0;
}



测试用例:
在这里插入图片描述

结果:输出哈夫曼树,并存储到文件里

在这里插入图片描述

输出文章的解码:(取一小部分展示)

在这里插入图片描述

  • 5
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要建立一个文本文件a,并统计该文件中各字符的频率,然后对这些字符进行Huffman编码,可以按照以下步骤进行: 1. 创建一个文本文件a,并在文件中写入要进行统计编码的文本内容。 2. 读取文件a的内容,并将每个字符出现的频率记录下来。可以使用一个字典来存储每个字符及其对应的出现次数。 3. 根据字符出现频率构建Huffman树。 a. 创建一个节点列表,将所有的字符作为叶子节点,并以它们的出现频率作为权值。 b. 从节点列表中选择两个权值最小的节点,创建一个新的节点作为它们的父节点,并将父节点的权值设为两个子节点的权值之和。 c. 将新的父节点插入到节点列表中,同时删除原先的两个子节点。重复这个步骤,直到节点列表中只剩下一个节点,即根节点。 4. 根据构建好的Huffman树,为每个字符生成对应的Huffman编码。 a. 从根节点出发,依遍历Huffman树的左右子树,当遍历到叶子节点时,记录下从根节点到该叶子节点的路径上的0和1,为该字符生成对应的编码。 b. 将每个字符及其对应的Huffman编码存储到一个字典中。 5. 将统计得到的字符频率和字符对应的Huffman编码写入到一个新的文本文件b中。 通过以上步骤,我们就可以建立一个文本文件a并统计其中各字符的频率,然后根据这些频率进行Huffman编码,并将结果写入到另一个文本文件b中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值