数据结构学习:C语言哈夫曼编译码

这是一个C++程序,实现了哈夫曼编码的创建、编码文件的保存和读取、解码等功能。程序首先读取数据文件,统计字符出现次数,然后建立哈夫曼树,生成哈夫曼编码,并将编码保存到文件。此外,程序还能从编码文件读取信息并解码。文件操作部分指定在D盘,可以自定义修改路径。
摘要由CSDN通过智能技术生成

这是学校的一篇程序设计题,哈夫曼编译码,里面包含一些对文件的操作
其中哈夫曼树的部分,是移植了网上一篇博主的代码,自己添加文件和译码的部分,下面做一些简单的说明。
1.#include
using namespace std;
这部分因为我用的编译器是vscode,在使用gets函数时报错,所以网上查到了加这个头文件,是C++的写法,cin.getline
2.因为作业要求,所以只写了a-h的译码,大家可以自行添加别的字符
3.文件操作放在D盘,大家也可以自行更改

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<iostream>
 using namespace std; 

#define x 7
#define n 8  //叶子数目
#define m (2*n-1)    //结点总数
#define maxval 10000.0
#define maxsize 100   //哈夫曼编码的最大位数
typedef struct
{
 char ch;
 float weight;
 int lchild,rchild,parent;
}hufmtree;
typedef struct
{
 char bits[x];   //位串
 int start;      //编码在位串中的起始位置
 char ch;        //字符
}codetype;

char buf[maxsize];  /*数据文件缓冲区*/
char buf1[maxsize]; /*编码文件缓冲区*/
int num[maxsize];   /*字符个数*/
int len,length;     /*字符长度和编码长度*/
//char temp[maxsize]; 
char table[maxsize]; /*字符内容*/

void huffman(hufmtree tree[]);  //建立哈夫曼树
void huffmancode(codetype code[],hufmtree tree[]);  //根据哈夫曼树求出哈夫曼编码
void decode(hufmtree tree[]);  //依次读入电文,根据哈夫曼树译码
void PrintHFMT (hufmtree tree[]);  //展示哈夫曼树
void OpenFile (void);    //打开数据文件
void ReadFile(void);     //读取编码文件
void savecode(codetype code[]); //保存编码文件
void savetobe(codetype code[],hufmtree tree[]); //保存原文文件

int main()
{
 printf("总共有%d种字符\n",n);
 hufmtree tree[m];
 codetype code[n];
 int flag;

    printf(".................哈夫曼编码系统..............\n");
    printf(".................1.读取数据文件..............\n");
    printf(".................2.编码并保存................\n");
    printf(".................3.读取编码文件..............\n");
    printf(".................4.解码并保存................\n");
    printf(".................5.展示哈夫曼树..............\n");
    printf(".................6.退出代码..................\n");
while(1)
{
    scanf("%d",&flag);
    if(flag == 1)
    {
        OpenFile ();
    }
    if(flag == 2)
    {
        huffman(tree);//建立哈夫曼树
        huffmancode(code,tree);//根据哈夫曼树求出哈夫曼编码
        printf("【输出每个字符的哈夫曼编码】\n");
        savecode(code);
    }
    if(flag == 3)
    {
        ReadFile();
    }
    if(flag == 4)
    {
        savetobe(code,tree);   
    }
    if(flag == 5)
    {
        PrintHFMT(tree);
    }
    if(flag == 6)
    {
        exit(1);
    }
}

 //decode(tree);//依次读入电文,根据哈夫曼树译码


 return 0;
}

void OpenFile (void)
{
    int num_a = 0,num_b = 0,num_c = 0;
	//char buf[MAX_LINE];  /*缓冲区*/
	FILE* fp;            /*文件指针*/
	if ((fp = fopen("D://DataFile.txt", "r")) == NULL)
	{
		perror("fail to read");
		exit(1);
	}
	while (fgets(buf, maxsize, fp) != NULL)
	{
		len = strlen(buf);
		buf[len] = '\0';  /*去掉换行符*/
		printf("数据文件:%s  长度:%d\n", buf,len);
	}

    for(int i=0;buf[i];i++)
    {
        if(buf[i] == 'a')
        {
            num[0]++;
            table[0] = 'a';
        }

        if(buf[i] == 'b')
        {
            num[1]++;
            table[1] = 'b';
        }
       
        if(buf[i] == 'c')
        {
            num[2]++;
            table[2] = 'c';
        }
        if(buf[i] == 'd')
        {
            num[3]++;
            table[3] = 'd';
        }
        if(buf[i] == 'e')
        {
            num[4]++;
            table[4]='e';
        }
        if(buf[i] == 'f')
        {
            num[5]++;
            table[5]='f';
        }
        if(buf[i] == 'g')
        {
            num[6]++;
            table[6] = 'g';
        }
        if(buf[i] == 'h')
        {
            num[7]++;
            table[7] = 'h';
        }
    }

}

void ReadFile(void)
{
   // int length = 0;
    FILE *fp;
   fp = fopen("D://CodeFile.txt", "r");
   if(fp == NULL)
   {
      printf("Error!");
      exit(1);
   }

while (fgets(buf1, maxsize, fp) != NULL)
	{
		length = strlen(buf1);
		buf1[length] = '\0';  /*去掉换行符*/
		printf("读到的编码:%s 编码长度:%d\n", buf1,length );
	}

   fclose(fp);
}
void savecode(codetype code[])
{
    FILE *fpn;
 fpn = fopen("D://Codefile.txt", "w");
   if(fpn == NULL)
   {
      printf("Error!");
      exit(1);
   }
 for(int i=0;i<n;i++)
 {
  //printf("%c: ",code[i].ch);
    for(int j=code[i].start;j<n;j++)
    {
        printf("%c",code[i].bits[j]);
        //temp[j] = code[i].bits[j];
        //printf("%c",temp[j]);
       fputc(code[i].bits[j],fpn);
    }
 }
 printf("\n");
 fclose(fpn);
}
void huffman(hufmtree tree[])//建立哈夫曼树
{
 int i,j,p1,p2;//p1,p2分别记住每次合并时权值最小和次小的两个根结点的下标
 float small1,small2,f;
 char c;
 for(i=0;i<m;i++)    //初始化
 {
  tree[i].parent=0;
  tree[i].lchild=-1;
  tree[i].rchild=-1;
  tree[i].weight=0.0;
 }
 //printf("【依次读入前%d个结点的字符及权值(中间用空格隔开)】\n",n);
 for(i=0;i<n;i++)  //读入前n个结点的字符及权值
 {
 // printf("输入第%d个字符为和权值",i+1);
   // scanf("%s",&c);
    //getchar();
  tree[i].ch=table[i];
  tree[i].weight=num[i];

 }
 for(i=n;i<m;i++)      //进行n-1次合并,产生n-1个新结点
 {
  p1=0;p2=0;
  small1=maxval;small2=maxval;   //maxval是float类型的最大值
  for(j=0;j<i;j++)    //选出两个权值最小的根结点
   if(tree[j].parent==0)
    if(tree[j].weight<small1)
    {
     small2=small1;  //改变最小权、次小权及对应的位置
     small1=tree[j].weight;
     p2=p1;
     p1=j;
    }
    else
     if(tree[j].weight<small2)
     {
      small2=tree[j].weight;  //改变次小权及位置
      p2=j;
     }
  tree[p1].parent=i;
  tree[p2].parent=i;
  tree[i].lchild=p1;  //最小权根结点是新结点的左孩子
  tree[i].rchild=p2;  //次小权根结点是新结点的右孩子
  tree[i].weight=tree[p1].weight+tree[p2].weight;
 }
}//huffman
void PrintHFMT (hufmtree tree[])            	/*输出向量状态表*/
{	
     
    int i,k=0;
    printf("\n哈夫曼树的各边显示:\n");
	for (i=0; i<2*n-1; i++)
        while(tree[i].lchild!=-1)
		{   		 
            printf("(%.0f,%d),(%.0f,%d)\n",tree[i].weight,tree[i].lchild,tree[i].weight,tree[i].rchild);
			break;
		}
}

void huffmancode(codetype code[],hufmtree tree[])//根据哈夫曼树求出哈夫曼编码
//codetype code[]为求出的哈夫曼编码
//hufmtree tree[]为已知的哈夫曼树
{
 int i,c,p;
 codetype cd;   //缓冲变量
 for(i=0;i<n;i++)
 {
  cd.start=n;
  cd.ch=tree[i].ch;
  c=i;       //从叶结点出发向上回溯
  p=tree[i].parent;   //tree[p]是tree[i]的双亲
  while(p!=0)
  {
   cd.start--;
   if(tree[p].lchild==c)
    cd.bits[cd.start]='0';   //tree[i]是左子树,生成代码'0'
   else
    cd.bits[cd.start]='1';   //tree[i]是右子树,生成代码'1'
   c=p;
   p=tree[p].parent;
  }
  code[i]=cd;    //第i+1个字符的编码存入code[i]
 }
}//huffmancode

void decode(hufmtree tree[])//依次读入电文,根据哈夫曼树译码
{
 int i,j=0;
 char b[maxsize];
 char endflag='2';    //电文结束标志取2
 i=m-1;             //从根结点开始往下搜索
// printf("输入发送的编码(以'2'为结束标志):");
 //cin.getline(buf,20);
      for(int i = 0; i < length; i++)
    {
        b[i]=buf1[i];
    }
    b[length+1] = '2';
printf("译码后的字符为");
 while(b[j]!='2')
 //for(j = 0; j < 12; )
 {
  if(b[j]=='0')
   i=tree[i].lchild;   //走向左孩子
  else
   i=tree[i].rchild;   //走向右孩子
  if(tree[i].lchild==-1)     //tree[i]是叶结点
  {
   printf("%c",tree[i].ch);
  i=m-1;      //回到根结点
  }
  j++;
 }
 printf("\n");
 if(tree[i].lchild!=-1&&buf[j]!='2')   //电文读完,但尚未到叶子结点
 //if(j > length || j < length)
  printf("\nERROR\n");  //输入电文有错
}
void savetobe(codetype code[],hufmtree tree[])
{
    char b[maxsize];
    FILE *fpn;
    fpn = fopen("D://ToBeTran.txt", "w");
   if(fpn == NULL)
   {
      printf("Error!");
      exit(1);
   }

    int i,j=0;
    char endflag='2';    //电文结束标志取2
    i=m-1;             //从根结点开始往下搜索
    //printf("输入发送的编码(以'2'为结束标志):");
    //cin.getline(b,length);
    for(int i = 0; i < length; i++)
    {
        b[i]=buf1[i];
    }
    b[length + 1] = '2';
    printf("译码后的字符为");
    while(b[j]!='2')
    //for(j = 0; j < length; )
    {
        if(b[j]=='0')
        i=tree[i].lchild;   //走向左孩子
        else
        i=tree[i].rchild;   //走向右孩子
        if(tree[i].lchild==-1)     //tree[i]是叶结点
        {
          // printf("%c",tree[i].ch);
            if(tree[i].ch == 'a')
            {
                for(int k = 0; k < num[0]; k++)
                {
                    printf("a");
                    fputc('a',fpn);
                }
            }
            if(tree[i].ch == 'b')
            {
                for(int k = 0; k < num[1]; k++)
                {
                    printf("b");
                    fputc('b',fpn);
                }
            }
            if(tree[i].ch == 'c')
            {
                for(int k = 0; k < num[2]; k++)
                {
                    printf("c");
                    fputc('c',fpn);
                }
            }
            if(tree[i].ch == 'd')
            {
                for(int k = 0; k < num[3]; k++)
                {
                    printf("d");
                    fputc('d',fpn);
                }
            }
            if(tree[i].ch == 'e')
            {
                for(int k = 0; k < num[4]; k++)
                {
                    printf("e");
                    fputc('e',fpn);
                }
            }
            if(tree[i].ch == 'f')
            {
                for(int k = 0; k < num[5]; k++)
                {
                    printf("f");
                    fputc('f',fpn);
                }
            }
            if(tree[i].ch == 'g')
            {
                for(int k = 0; k < num[6]; k++)
                {
                    printf("g");
                    fputc('g',fpn);
                }
            }
            if(tree[i].ch == 'h')
            {
                for(int k = 0; k < num[7]; k++)
                {
                    printf("h");
                    fputc('h',fpn);
                }
            }
            i=m-1;      //回到根结点
        }
        j++;
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

地球先生_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值