【数据结构】哈夫曼树实现编码译码


       根据一段字符串中字符的个数 作为该字符的权值生成哈夫曼树。

       然后根据生成的哈夫曼编码,对任意字符串实现编码,对任意二进制串实现译码。

程序运行结果:


1.程序主界面:

【数据结构】哈夫曼树实现编码译码_第1张图片

2.根据字符串 创建哈夫曼树及编码:

【数据结构】哈夫曼树实现编码译码_第2张图片

3.生成的编码表如下:

【数据结构】哈夫曼树实现编码译码_第3张图片

4.根据生成的哈夫曼编码对字符串编码:

【数据结构】哈夫曼树实现编码译码_第4张图片

5.生成的编码保存在文件中:

【数据结构】哈夫曼树实现编码译码_第5张图片

6.对二进制串译码:

【数据结构】哈夫曼树实现编码译码_第6张图片



结果:

【数据结构】哈夫曼树实现编码译码_第7张图片




代码:

哈夫曼树的生成和编码的常见,以及编码和译码函数

//_HuffmanTree_H
#ifndef _HuffmanTree_H
#define _HuffmanTree_H

typedef struct
 {
   unsigned int weight;
   char ch;
   unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree; 
 typedef char **HuffmanCode; 

//这三个是哈夫曼树的生成
int min1(HuffmanTree t,int i);     //选出权值数组中最小的一个 ,,

void select(HuffmanTree t,int i,int *s1,int *s2);   //权值数组中最小的两个,,,并把临时数组对应位置改为1防止重复选择

void HuffmanCoding(HuffmanTree *HT,HuffmanCode *HC,int *w,int n);  //根据权值数组W,和元素个数N开辟空间,HC存放编码

void getWeightInfile(int *w);// 从MyTxt.TXT里读入字母,以字母个数为权值,生成数组并返回

void bianMaInFile(HuffmanTree *p,HuffmanCode cd);    //根据编码.txt里面字母,找到对应的下标,在hc里面匹配编码

void yimaInFile(HuffmanTree *p);   //分析出  数的根结点下标应该为2n-1。。。所以只需根据编码 0 1确定向左还是向右 最终直到=遇到有意义的字母停止



#endif  //

对指定文件的读和写

//myfile.h
#ifndef _MYFILE_H
#define _MYFILE_H
#define MY_STRMAX 100

void changMyTxtFile();
void showMyTxtFile();
void showBianMaFile();
void showYiMaFile();
void changBianMaFile();

#endif   //myfile.h

//HuffmanTree.c
#include 
#include 
#include 

#include "HuffmanTree.h"



int min1(HuffmanTree t,int i)
 { /* 返回i个结点中权值最小的树的根结点序号,函数select()调用 */
	int j,flag;
	unsigned int k=10000000; /* 取k为不小于可能的值(无符号整型最大值) */
	for(j=1;j<=i;j++)
		if(t[j].weight*s2)
	{
		j=*s1;
		*s1=*s2;
		*s2=j;
	}
 }

void HuffmanCoding(HuffmanTree *HT,HuffmanCode *HC,int *w,int n) /* 算法6.12 */
{ /* w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC */
	int m,i,s1,s2,start,num=0;
	unsigned c,f;
	HuffmanTree p;
	char *cd;
	if(n<=1)
		return;
	m=2*n-1;    //开辟的空间 比元素个数多 因为后续生成的新子树要用到
	*HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); /* 0号单元未用 */
	for(p=*HT+1,i=1;i<=n;++i,++p,++w,++num)
	{
		(*p).weight=*w; //这部分是给确定的子树赋值,包括字母a-z 权值从数组中得出
			if(i<26)
				(*p).ch=97+num;
			else if(i==26)
			{
				(*p).ch=97+num;
				num=-1;
			}
			else if(i<=52&&i>26)
				(*p).ch=65+num;
			else if(i==53)
				(*p).ch=32;
			else if(i==54)
				(*p).ch='\n';
		(*p).parent=0;
		(*p).lchild=0;
		(*p).rchild=0;
	}
	for(;i<=m;++i,++p)
		(*p).parent=0;
	for(i=n+1;i<=m;++i) /* 建赫夫曼树 */
	{ /* 在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2 */
		select(*HT,i-1,&s1,&s2);
		(*HT)[s1].parent=(*HT)[s2].parent=i;     //这部分给后面要用到的新节点 初值
		(*HT)[i].lchild=s1;
		(*HT)[i].rchild=s2;
		(*HT)[i].ch='!';                        //他们存放的是!   所以可以判断此节点是否有意义
		(*HT)[i].weight=(*HT)[s1].weight+(*HT)[s2].weight;
	}


//哈夫曼 编码 的求出  保存在HC里面(二维数组)



	/* 从叶子到根逆向求每个字符的赫夫曼编码 */
	*HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
	/* 分配n个字符编码的头指针向量([0]不用) */
	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]); /* 从cd复制编码(串)到HC */
	}
	free(cd); /* 释放工作空间 */
}


void getWeightInfile(int *w)    
{
	FILE *pfr;
	int i=0;
	pfr = fopen("记录信息\\mytxt.txt", "r");//读取文件
	if (pfr==NULL)
	{
		printf("文件操作失败");
		return ;
	}
	else
	{
		while (!feof(pfr))     //直到文件读取完毕,每次读取一行
		{
			char str[256] = {0};
			fgets(str, 256, pfr);//读取一行
			i=0;
			while(str[i]!=0)    //如果这是个字母,那么对应数组的从0-25 发现一个 对应的值加加  实现计算文件中字母的个数
			{	
				if(str[i]<='z' && str[i]>='a')      //97-122   65-90  32
					w[str[i]-97]++;
				if(str[i]<='Z'&& str[i]>='A')
					w[str[i]-65+26]++;
				if(str[i]==' ')
					w[26*2]++;
				if(str[i]=='\n')
					w[26*2+1]++;
				i++;
				
			}			
		}
	}
	fclose(pfr);
}                    //文件用完要关闭



void bianMaInFile(HuffmanTree *p,HuffmanCode cd)
{
	FILE *pfr;
	FILE *pfw;
	//HuffmanTree ptr=(*p)+1;
	int i;
	int num;
	int count=1;
	pfr = fopen("记录信息\\mytxt.txt", "r");//读取文件
	pfw = fopen("记录信息\\编码.txt","w");//写入文件
	if (pfr==NULL || pfw==NULL)
	{
		printf("文件操作失败");
		return ;
	}
	else 
	{
		while (!feof(pfr))
		{
			char str[256] = {0};
			fgets(str, 256, pfr);//读取一行
			i=0;
			num=-1;
			while(str[i]!=0)
			{	
				
				if(str[i]<=122 && str[i]>=97)     //1-26
							num=str[i]-96;
				else if(str[i]<=90 && str[i]>=65)  //27-52
							num=str[i]-64+26;
				else if(str[i]==32)   
							num=53;
				else if(str[i]=='\n')
							num=54;
				if(num!=-1)//就把cd里面的编码 输出到 编码.txt里面
				{
					if(count==1)
						count++;
					else
						//fputs(cd[num], pfw); 
						fprintf(pfw,"%s\n",cd[num]);
				}
				i++;
				num=-1;
				
			}			
		}
		fclose(pfr);
		fclose(pfw);
	}
}

void yimaInFile(HuffmanTree *p)
{
	FILE *pfr;
	FILE *pfw;
	char s[2]={0};
	int i=0,num=107;  //51=2*26-1    2*54-1
	int n;
	pfr = fopen("记录信息\\编码.txt", "r");//读取文件
	pfw = fopen("记录信息\\译码.txt","w");//写入文件
	if (pfr==NULL || pfw==NULL)
	{
		printf("文件操作失败");
		return ;
	}
	else 
	{
		while (!feof(pfr))
		{
			char str[255] = {0};			
			fgets(str, 255, pfr);
			i=0;//读取一行
			num=107;
			while(str[i]!=0)
			{	n=i;
				while(str[i]!=0)
				{
					if((*p)[num].ch!='!')            //如果发现 左右移动找到了字母 立马退出 匹配下一个
						break;
					if(str[i]=='0')                 //0左1右
						num=(*p)[num].lchild;
					else if(str[i]=='1')
						num=(*p)[num].rchild;					
					i++;
				}
				if((*p)[num].ch=='!' && str[i]!='\0')
					printf("因为编码不全,%d-%d个编码被舍掉\n",n+1,i);
				else if(str[i]!='\0')
				{	
					s[0]=(*p)[num].ch;
					fputs(s, pfw);
				}
				num=107;
				
			}
		}
	}
	fclose(pfr);
	fclose(pfw);
}


//myfile.c
#include 
#include 
#include "myfile.h"
#include 

void changMyTxtFile()
{
	FILE *pfw;
	int s;
	int num=1;
	pfw=fopen("记录信息\\mytxt.txt","w");
	if (pfw==NULL)
	{
		printf("文件操作失败");
		return;
	}
	while(s=getchar(),s!=-1)
	{
		fprintf(pfw,"%c",s);
	}
	fclose(pfw);
}
void showMyTxtFile()
{
	FILE *pfr;
	pfr=fopen("记录信息\\mytxt.txt","r");
	if (pfr==NULL)
	{
		printf("文件操作失败");
		return ;
	}
	else 
	{	
		while (!feof(pfr))
		{
			char str[256] = {0};			
			fgets(str, 256, pfr);          //动态  遍历文件
			puts(str);
		}
	}
	fclose(pfr);
}

void showBianMaFile()
{
	FILE *pfr;
	pfr=fopen("记录信息\\编码.txt","r");
	if (pfr==NULL)
	{
		printf("文件操作失败");
		return ;
	}
	else 
	{	
		while (!feof(pfr))
		{
			char str[256] = {0};			
			fgets(str, 256, pfr);
			puts(str);
		}
	}
	fclose(pfr);
}

void showYiMaFile()
{
	FILE *pfr;
	pfr=fopen("记录信息\\译码.txt","r");
	if (pfr==NULL)
	{
		printf("文件操作失败");
		return ;
	}
	else 
	{	
		while (!feof(pfr))
		{
			char str[256] = {0};			
			fgets(str, 256, pfr);
			puts(str);
		}
	}
	fclose(pfr);
}

void changBianMaFile()
{
	
	FILE *pfw;
	int s;
	pfw=fopen("记录信息\\编码.txt","w");
	if (pfw==NULL)
	{
		printf("记录信息\\文件操作失败");
		return ;
	}
	while(s=getchar(),s!=-1)
	{
		fprintf(pfw,"%c",s);
	}
	fclose(pfw);
	
}

//main.c
#include 
#include 
#include "HuffmanTree.h"
#include "myfile.h"


#define MYHFMNUM 26*2+2

int  main()
{
	   HuffmanTree HT;
	   HuffmanCode HC;
	   int w[MYHFMNUM]={0},n=MYHFMNUM,i;
	   char s[MY_STRMAX]={0};
	   int num;
		system("color 3e");    //改颜色
MENU:
			system("cls");
	   puts("***********************************************");
	   puts("		1.创建新的编码");
	   puts("		2.编码");
	   puts("		3.译码");
	   puts("		4.退出");
	   puts("***********************************************");

	   puts("输入想要的操作:");
       scanf("%d",&num);
	   switch(num)
	   {
	   case 1: 
		   {	 FILE *pfw;	
			   system("cls");	 //每次操作清理屏幕
				puts("输入一段字符串,会根据字符数累加权值,从而生成树");
			   changMyTxtFile();
			   getWeightInfile(w);
			   HuffmanCoding(&HT,&HC,w,n);
			   pfw=fopen("记录信息\\编码表.txt","w");
			   if (pfw==NULL)
			   {
				   printf("文件操作失败");
				   return 0;
				}
					fprintf(pfw,"       编码    字母      权值\n");  
			   for(i=1;i<=n;i++) //把生成的   编码表  存在文件中 便于对照
			   {             
				   fprintf(pfw,"%10s\t%c\t%2d\n",HC[i],HT[i].ch,HT[i].weight);   
			   }

			   fclose(pfw);
				system("pause");
			   goto MENU;
		   }
	   case 2: 
		   {	system("cls");
			   puts("输入要编码的字符串");
			   changMyTxtFile();
			   bianMaInFile(&HT,HC); 
			   showBianMaFile();
			   system("pause");
			   goto MENU;
		   }
	   case 3:
			{	system("cls");
			   puts("输入要译码的二进制数字");
			   changBianMaFile();
			   yimaInFile(&HT);
			   showYiMaFile();
				system("pause");    //暂停一下  相当于getch
			   goto MENU;
		   }
	   case 4:return 0;
	   default:puts("输入有误!");system("pause");goto MENU;           
	   }
	   
	   return  0;
}


你可能感兴趣的:(c语言基础)