[数据结构]哈夫曼编码实现文本压缩

要求:输入字符串,输出字符串的哈夫曼编码

输入:一行字符串。

输出:n行,n为包含的不同字符数

         每行输出该字符的出现次数和编码。

            最后一行输出该字符串转换成哈夫曼编码的形式。

实验结果:

  

 /*补充知识:哈夫曼树

 

  树的带权路径长度

    设二叉树具有n个带权叶结点,从根结点到各叶结点的路径长度与相应叶节点权值的乘积之和称为 树的带权路径长度(Weighted Path Length of Tree,WPL)

    设wi为二叉树地l个叶结点的权值,li为从根结点到第i个叶结点的路径长度,则 WPL 计算公式如下:

   

    

 

  结构

    对于给定一组具有确定权值的叶结点,可以构造出不同的二叉树,其中,WPL 最小的二叉树 称为 霍夫曼树(Huffman Tree)

    对于霍夫曼树来说,其叶结点权值越小,离根越远,叶结点权值越大,离根越近。

 

  霍夫曼算法

     霍夫曼算法用于构造一棵霍夫曼树,算法步骤如下:    

  1. 初始化:由给定的n个权值构造n棵只有一个根节点的二叉树,得到一个二叉树集合F
  2. 选取与合并:从二叉树集合F中选取根节点权值 最小的两棵 二叉树分别作为左右子树构造一棵新的二叉树,这棵新二叉树的根节点的权值为其左、右子树根结点的权值和。
  3. 删除与加入:从F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到F中。
  4. 重复 2、3 步,当集合中只剩下一棵二叉树时,这棵二叉树就是霍夫曼树。

   

 

   霍夫曼编码

    霍夫曼树可用于构造 最短的前缀编码,即 霍夫曼编码(Huffman Code),其构造步骤如下:

     

     

*/

   OK,进入正题,我们来看看如何通过上面的步骤来将字符串转化为哈夫曼编码。

 

实验步骤:

   

  将字符串读进s数组后,进入calcu函数。在calcu函数中,我们将输入的字符串中每个字符映射到以其ASCII码为下标的数组arr中,用arr数组记录每个字符出现的次数,比如arr[x]=3说明ASCII为x的字符出现了3次,我们就知道了每个字符所占的权重,方便接下来的createHuffmanTree()中哈夫曼以结点权重建树。

  第二个for循环统计有多少字符出现过,就知道了哈夫曼树中有多少叶子结点。

   

  建树中,记录每个结点的权重weight叶子结点即为字符出现次数,非叶子结点为以其为根的子树上叶子结点权重之和。value结点记录叶子结点实际代表的字符。

     

   在建树时,首先通过arr数组将其中所有出现过的字符,构建一个单独的结点,并且全部归纳在数组forest数组中,这样forest数组就存储了一个全部结点的森林,接下来我们来对结点进行合并,最终构建一棵哈夫曼树。

   

   接下来通过n-1次循环,每次合并两个结点。通过两个for循环在forest森林中找到权值最小的结点minn和次小的结点minnsub,然后将他们合并在一个空的根节点上,形成一棵子树,并放入forest森林中,同时删除在森林中删除这两个结点。子树的根节点记录该树的权值,即两结点权值之和。然后继续在森林中继续找权值最小的两子树合并。当只剩下一棵二叉树时,这棵二叉树就是霍夫曼树。

   

  接下来就可以通过刚才建好的哈夫曼树计算哈夫曼编码了。如果只有一种字符,那么直接输出。否则对该树进行深度优先遍历,如果是向左子树遍历,向编码中加入一个0,如果是向右遍历在编码中加入1。当找到叶子结点是,说明找到了字符输出它出现的次数weight和编码,同时将该字符的编码方式记录到diction字典中,方便接下来对照将字符串中每个字符转成编码。

  

 

  

  遍历字符串将每个字符送入trans函数将其转化为编码输出。

  于是,大功告成,字符串被转换成了哈夫曼编码!!!

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
char s[1000005];
int arr[150],N,diction[200][200];//ASCII有128位

typedef struct HNode {
  int weight;
  char value;
  HNode *lchild, *rchild;
} * Htree; 

void calcu(int l)
{
    for(int i=0;i<l;i++)
    {
        char x=s[i];
        arr[x]++; 
    }
    for(int i=1;i<=128;i++)
        if(arr[i]!=0)N++;
}

Htree createHuffmanTree(int arr[], int n) 
{
    int x=0;
    Htree forest[n];
      Htree root = NULL;
      for(int i=0;i<n;i++) //将所有点存入森林
    {  
        if(arr[i]==0)continue;
        Htree temp;
        temp=(Htree)malloc(sizeof(HNode));
        temp->weight=arr[i];
        temp->value=i;
        temp->lchild=temp->rchild = NULL;
        forest[x]=temp;
        x++;
      }
      n=x;//n种字符 
    for(int i=1;i<n;i++) 
    {  // n-1 次循环建哈夫曼树
        int minn=-1,minnSub;  // minn 为最小值树根下标,minnsub 为次小值树根下标
        for(int j=0;j<n;j++) 
        {
              if(forest[j]!=NULL&&minn==-1) 
            {
                minn=j;
                continue;
              }
              if(forest[j]!=NULL) 
            {
                minnSub=j;
                break;
              }
        }
        for(int j=minnSub;j<n;j++) 
        {  // 根据 minn 与 minnSub 赋值
              if(forest[j]!=NULL) 
            {
                if(forest[j]->weight<forest[minn]->weight) 
                {
                      minnSub=minn;
                      minn=j;
                } 
                else if(forest[j]->weight<forest[minnSub]->weight) 
                {
                      minnSub=j;
                }
              }
        }
        // 建新树
        root=(Htree)malloc(sizeof(HNode));
        root->weight=forest[minn]->weight + forest[minnSub]->weight;
        root->lchild=forest[minn];
        root->rchild=forest[minnSub];

        forest[minn]=root;     // 指向新树的指针赋给 minn 位置
        forest[minnSub]=NULL;  // minnSub 位置为空
      }
      return root;
}

void huffmanCoding(Htree root,int x,int arr[])  // 计算霍夫曼编码
{ 
    if(N==1)
    {
        printf("结点为 %c 的字符出现次数为:%-4d 编码为: 1",s[0],arr[s[0]]);
        return ;
    }
      if(root!=NULL) 
      {
        if(root->lchild==NULL&&root->rchild==NULL) 
        {
              printf("结点为 %c 的字符出现次数为:%-3d 编码为: ",root->value, root->weight);
              for (int i=0;i<x;i++) 
              {
                  diction[root->value][i+1]=arr[i];
                  printf("%d",arr[i]);
            }
              diction[root->value][0]=x;
              printf("\n");
        }     
        else 
        {
              arr[x]=0;
              huffmanCoding(root->lchild,x+1,arr);
              arr[x]=1;
              huffmanCoding(root->rchild,x+1,arr);
        }
    }
}
void trans(int c)
{
    for(int i=1;i<=diction[c][0];i++)
        printf("%d",diction[c][i]); 
} 
int main()
{
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    XXXXXXXXXXXXXX 开动脑筋哦 XXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
}

 

 

 

 

posted @ 2022-04-29 00:05  wisdom_jie  阅读(530)  评论(0编辑  收藏  举报