哈夫曼编码的两种实现(实验报告)

本文详细介绍了基于27个字符的哈夫曼编码实验过程,包括哈夫曼树的构建、编码与译码的实现,以及文件存储和读取功能。实验通过C语言完成,涉及数据结构和算法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构的某项实验

① 问题描述:给定n个字符的权值数组w,根据哈夫曼编码与译码规则,实现一个哈夫曼编/译码系统(利用实验指导书上的27个字符的数据进行实验)。
② 利用顺序表存储Huffman树,编码结果的存储方式采用书上的结构。
③ Huffman树的构造约定如下:
根的权值较小的子树作为左子树,当权值相等时,则先生成的子树是左子树;
按照结点的生成次序选择权值较小的两棵子树构造Huffman树;
从叶子结点到根结点逆向求出每个字符的Huffman编码,不采用递归方法;
从根结点开始实现译码,要求被译码的字符数大于20个字符。
④ 采用文件方式存储n个权值和待翻译的二进制代码,其余数据均不采用文件存储。

序号 字符 权值 双亲结点 左孩子 右孩子
1 □ 186 0 0 0
2 A 64 0 0 0
3 B 13 0 0 0
4 C 22 0 0 0
5 D 32 0 0 0
6 E 103 0 0 0
7 F 21 0 0 0
8 G 15 0 0 0
9 H 47 0 0 0
10 I 57 0 0 0
11 J 1 0 0 0
12 K 5 0 0 0
13 L 32 0 0 0
14 M 20 0 0 0
15 N 57 0 0 0
16 O 63 0 0 0
17 P 15 0 0 0
18 Q 1 0 0 0
19 R 48 0 0 0
20 S 51 0 0 0
21 T 80 0 0 0
22 U 23 0 0 0
23 V 8 0 0 0
24 W 18 0 0 0
25 X 1 0 0 0
26 Y 16 0 0 0
27 Z 1 0 0 0

  1. 实验过程与结果
    完整代码:(实验环境codeblock)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//左0右1
typedef struct
   {
      unsigned int weight;
      unsigned int parent,lchild,rchild;
      char c;
      int length;
  }HTNode, *HuffmanTree;
typedef char **HuffmanCode;

void save(int n,int w[],char code[],char ch[])
{
	FILE *fp;
	char filename[20];
	int i;
	printf("请输入要保存的文件名称:\n");
	scanf("%s",filename);
	if((fp=fopen(filename,"wb"))= =NULL)//打开输出文件
	{
		printf("cannot open file:\n");
		return;
	}
	for(i=0;i<=n;i++)
	{
		if(fwrite(&w[i],sizeof(int),1,fp)!=1)//数组向磁盘文件写入各叶子节点权值
			printf("file write error!\n");
	}
	for(i=0;i<=n;i++)
    {
        if(fwrite(&ch[i],sizeof(char),1,fp)!=1)//数组向磁盘文件写入各叶子字母代表
			printf("file write error!\n");
    }
	for(i=0;i<=strlen(code);i++)
    {
      if(fwrite(&code[i],sizeof(char),1,fp)!=1)//数组向磁盘文件写入待破解电码代码
			printf("file write error!\n");
    }
	    printf("保存文件成功!!!");
	fclose(fp);
}
void read(int n,int w[],char code[],char ch[])
{
    FILE *fp;
    char filename[20];
    int i;
    printf("请输入读入的文件名:\n");
    scanf("%s",filename);
	if((fp=fopen(filename,"rb"))= =NULL)//以只读方式打开二进制文件
	{   printf("can not open file\n");
		fclose(fp);
	}
	for(i=0;i<=n;i++)
    fread(&w[i],sizeof(int),1,fp);//磁盘文件向数组读入
    for(i=0;i<=n;i++)
    fread(&ch[i],sizeof(char),1,fp);
    for(i=0;i<=strlen(code);i++)
    fread(&code[i],sizeof(char),1,fp);
    printf("读入数据成功!!!!");
    fclose(fp);
}
  void createHuffmanTree(HuffmanTree \*HT, int w[], int n,char ch[])
  {
     int m = 2 * n - 1;
     int s1,s2,i;
     \*HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));
     for(i = 1; i <= n; i++)
     { (\*HT)[i].weight = w[i]; (\*HT)[i].lchild = 0;
          (\*HT)[i].parent = 0;(\*HT)[i].rchild = 0;
           (\*HT)[i].c=ch[i]; (\*HT)[i].length=0;
      }
     for(i = n + 1; i <= m; i++)
      {(\*HT)[i].weight = 0;(\*HT)[i].lchild = 0;
         (\*HT)[i].parent = 0;(\*HT)[i].rchild = 0;
          (\*HT)[i].c='\0'; (\*HT)[i].length=0;
     }
     printf("\n构建哈夫曼树: \n");
     for(i = n + 1; i <= m; i++)
     { select(HT, i-1, &s1, &s2); (\*HT)[s1].parent = i;
         (\*HT)[s2].parent = i;(\*HT)[i].lchild = s1;
         (\*HT)[i].rchild = s2;
      (\*HT)[i].weight = (\*HT)[s1].weight + (\*HT)[s2].weight;
     } printf("\n");
 }
 void select(HuffmanTree \*HT, int n, int \*s1, int \*s2)
  { int i = 0,min;
      for(i = 1; i <= n; i++)
      {if((\*HT)[i].parent == 0)
          {min = i; break;
          }
      }
      for(i = 1; i <= n; i++)
     {if((\*HT)[i].parent == 0)
          { if((\*HT)[i].weight < (\*HT)[min].weight)
            { min = i;
            }
          }
      } \*s1 = min;
      for(i = 1; i <= n; i++)
      { if((\*HT)[i].parent == 0 && i != (\*s1))
          { min = i;break;
          }
     }
      for(i = 1; i <= n; i++)
      {if((\*HT)[i].parent == 0 && i != (\*s1))
          { if((\*HT)[i].weight < (\*HT)[min].weight)
              { min = i;
              }
          }   \*s2 = min;
 }
  }
//从叶子到根逆向求每个字符的哈夫曼编码
 void creatHuffmanCode(HuffmanTree HT,HuffmanCode HC,int n)
 {
     int i,j,c;
     int start;
     int f;
     HC=(HuffmanCode \*)malloc((n+1) * sizeof(char \*));//分配n个字符编码的头指针向量
     char \*cd=(char \*)malloc(n\*sizeof(char));//分配求编码的工作区间
     cd[n-1] = '\0';//从右向左逐位存放编码,先存放编码结束符
     for(i=1;i<=n;i++)//逐个字符求哈夫曼编码
     {      start = n - 1;
         for(c=i, f=HT[i].parent;f!=0;c=f, f=HT[f].parent)
         {      if(HT[f].lchild= =c)
                    cd[- -start]='0';
             else
                    cd[- -start]='1';
          }
         HC[i]=(char \*)malloc((n - start) * sizeof(char));   //为第i个字符编码分配空间
         HT[i].length=n-start-1;//求编码长度
         strcpy(HC[i],&cd[start]);
     } free(cd);//释放工作空间
     for(i = 1;i <= n; i++)
     {
         printf("权值为 %3d对应字母为  %c 的叶子节点的哈夫曼编码是 %s 长度为 %d \n", HT[i].weight,HT[i].c,HC[i],HT[i].length); }
 }
 //定义编码解码函数
void Translatecode(HuffmanTree HT,HuffmanCode HC,int n,char code[])
{  int i,j,m,c,choice;
     int start;
     int f;
     char zcode[100];//存放输入的字母字符串
     m=2*n-1;
     HC=(HuffmanCode *)malloc((n+1)*sizeof(char *));//分配n个字符编码的头指针向量
     char *cd=(char *)malloc(n*sizeof(char));//分配求编码的工作区间
     cd[n-1] = '\0';//从右向左逐位存放编码,先存放编码结束符
     for(i=1;i<=n;i++)//逐个字符求哈夫曼编码
     {  start = n - 1;
         for(c=i, f=HT[i].parent;f!=0;c=f, f=HT[f].parent)
         {  if(HT[f].lchild=\=c)
                    cd[- -start]='0';
             else
                    cd[- -start]='1';
          }
         HC[i]=(char \*)malloc((n - start) * sizeof(char));   //为第i个字符编码分配空间
         strcpy(HC[i],&cd[start]);
     }
     while(1)
    {  printf("\n是否进行编译操作。1-执行 2-退出:\n");
            scanf("%d",&choice);
            getchar();
            if(choice=\=1)
                {   printf("请输入需要编码的字母字符串 :\n");
                gets(zcode);
                 printf("编译好的二进制编码为:\n");
                 for(i=0;zcode[i]!='\0';i++)
                 for(j=1;j<=n;j++)
                 if(HT[j].c=\=zcode[i])
                 printf("%s",HC[j]);
                }
            else  if(choice=\=2)
                 break;
    }
      while(1){  printf("\n是否进行解码操作。1-执行 2-退出\n");
            scanf("%d",&choice);
            if(choice=\=1)
                {   j=0;
                 while(code[j]!='\0')
                 {  if(code[j]=\='0')
                       m=HT[m].lchild;
                     else    m=HT[m].rchild;
                     if(HT[m].lchild=\=0)//找到叶子节点m
                   {   printf("%c",HT[m].c);
                      m=2\*n-1;//回到根节点
                   }    j++;
                }
                getchar();
                  printf("\n原电码为:\n");
                 printf("%s",code);
                printf("\n请输入已破译的电文:\n");
                      gets(zcode);
                 printf("反编译的电码为:\n");
                 for(i=0;zcode[i]!='\0';i++)
                 for(j=1;j<=n;j++)
                 if(HT[j].c=\=zcode[i])
                 printf("%s",HC[j]);
   printf("\n判断电文输入正确否  1-正确  2-错误:\n");
               scanf("%d",&choice);
                    if(choice=\=1)
                        printf("\n电文输入正确!\n");
              else  printf("\n电文输入错误!\n");  }
            else  if(choice=\=2)
                 break;}
}
//定义获取叶子节点权值和字母的函数
void getcode(char ch[],int w[],int N[]){
    int i,j,count;
    int k=1;
    char temp;
    char zcode[50];//字母字符串
    printf("请输入字母字符串:\n");
    scanf("%s",zcode);
    for(i=0;zcode[i]!='\0';i++)
    {   if(zcode[i]=='*')
          continue;
          temp=zcode[i];
          count=0;
          for(j=i;zcode[j]!='\0';j++)
           { if(temp==zcode[j])
             {   zcode[j]='*';
                count++;
             }
             ch[k]=temp;//记录叶子节点代表的字母
             w[k]=count;//权值为字母出现频率
           }
            k++;  }
    N[0]=k-1;}
 void main()
 {  HuffmanTree HT;
     HuffmanCode HC;//各叶子节点的哈夫曼编码
     char ch[30];//各叶子节点的字符代表
     char code[400]; //存放输入的二进制编码字符串
     int w[30];//w数组存储权值
     int i,j,n,m,c;
     int N[1];//当执行0号操作时,作用等价于n的值
      HC=(HuffmanCode *)malloc((n+1)*sizeof(char *));
     while(1)
     { printf("\n0-输入叶子节点个数(采用2号方法建树时使用)");  printf("\n1-输入字母字符创建哈夫曼树");//0号操作与1号操作选择一个
     printf("\n2-输入数据的权值和相应的字符代表创建哈夫曼树");
         printf("\n3-输入要破解的电文密码");
         printf("\n4-文件存储信息");
         printf("\n5-文件读取信息");
         printf("\n6-构建哈夫曼树并打印哈夫曼树");
         printf("\n7-求各叶子节点的哈夫曼编码");
         printf("\n8-进行编码译码");
         printf("\n请输入你要执行的操作:\n");
         scanf("%d",&c);
         switch(c)
         {  case 0:
             printf("输入叶子节点个数 n:  " );
                  scanf("%d",&n);
                  m=2\*n-1;
                  break;
             case 1:
                   getcode(ch,w,N);
                        n=N[0];
                       m=2\*n-1;
                  break;
             case 2:
                  printf("\n请输入%d个数据的权值:\n",n);
                  for(i=1; i<=n; i++)
                     {   printf("%d: ",i);
                       fflush(stdin);//清空输入缓存区
                       scanf("%d",&w[i]);
                     }
                  printf("请输入%d个数据的字符代表:\n",n);
                  for(i=1;i<=n;i++)
                     {  printf("%d号为: ",i);
                        fflush(stdin);//清空输入缓存区
                        scanf("%c",&ch[i]);
                      }
                break;
             case 3:
                printf("请输入要破解的电文二进制编码:\n");
                scanf("%s",code);
                break;
             case 4:
                save(n,w,code,ch);
                break;
             case 5:
                read(n,w,code,ch);
                break;
             case 6:
                createHuffmanTree(&HT,w,n,ch);
           printf("\n\*\*\**\*\*\*\*\*\*打印哈夫曼树\*\*\**\*\*\*\*\n");
     printf("序号\t字符\t权值\t双亲\t左孩子\t右孩子\n");
                for(i=1;i<=m;i++)
                printf("%d\t%c\t%d\t%d\t%d\t%d\t\n",i,HT[i].c,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);
                break;
             case 7:
                creatHuffmanCode(HT,&HC,n);
                break;
             case 8:
                Translatecode(HT,&HC,n,code);
                break;
             default: break;
         }
         if((c<0)||(c>8))
             break;
    } }

运行结果:首先进行输入节点信息并存储文件的方法构建哈夫曼树

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
由于太长只截取部分编码。完整编码为000000001010011111100000000101001111000011110110001111110101111000011001100110110111001111010000110110010011111011110110011111110010000100101110000010111101000110011

保存文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关闭程序,重新开始,此时选择读取文件方式构建哈夫曼树,文件hafuman.txt中存储着各叶子节点权值,对应字符和待编译的二进制编码,读取文件截图如下,其他操作6.7.8及结果同上。
在这里插入图片描述
给出另一种构建哈夫曼树的方法

在这里插入图片描述
字母个数为相应字母对应的权值
在这里插入图片描述

其他操作同输入节点建树或者读取文件建树相似,在此不再给出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值