数据结构之哈夫曼编译码器(C语言版)

数据结构之哈夫曼编译码器的实现(C语言)

一枚学习编程的小白,前段时间在看数据结构,就把学到的整理一下下,有错误的地方希望大神可以指点嘻嘻嘻~

需求分析:一个完整的系统应具有以下功能

  1. 初始化: 从终端输入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存入文件hfmtree文件中。
  2. 编码: 利用已建立的哈夫曼树,对文件tobetran中的正文进行编码,然后将结果存入codefile中。
  3. 译码: 利用已建好的哈夫曼树将文件codefile中的代码进行译码,结果存入文件textfile中 。
  4. 印代码文件: 将文件codefile以紧凑格式显示在终端上,每行50个代码,同时将此字符形式的编码文件写入文件codeprint中。
  5. 印哈夫曼树: 将内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件treeprint中。
    测试数据:利用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“this program is my favorite”

要求!

哈夫曼树:
设有n个权值{w1,w2,……wn},构造一棵有n个叶子结点的二叉树,每个叶子的权值为wi,则wpl最小的二叉树叫哈夫曼树。

路径: 从树中一个结点到另一个结点之间的分支构成这两个结点间的~
路径长度:路径上的分支数
树的路径长度: 从树根到每一个结点的路径长度之和
树的带权路径长度: 树中所有带权结点的路径长度之和
公式
构造Huffman树的方法——Huffman算法
1、根据给定的n个权值{w1,w2,……wn},构造n棵只有根结点的二叉树,令其权值为wj
2、在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和
3、在森林中删除这两棵树,同时将新得到的二叉树加入森林中
重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树
Huffman编码:

思想: 根据字符出现频率编码,使电文总长最短
编码: 根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列

Huffman译码:

从Huffman树根开始,从待译码电文中逐位取码。若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束

没啥

基本知识说完之后,上代码!
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define leafnum 27
#define hufnum 2*leafnum 
#define maxdouble 9999.9

//哈夫曼树节点存储结构
typedef struct tnode  
{
	char name;
	double weight;
	int lchild,rchild,parent;
}huftree;

//哈夫曼编码表结构
typedef struct cnode      
{
	char bits[leafnum+1];
	int start;
	char ch;
}hufcode;

hufcode code[leafnum+1];
huftree tree[hufnum+1];
char huffmancode[1000];
//定义全局数组来存放这些字符名称和对应频度
char ch[] = {'\0',' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
float w[] = {0,186,64,13,22,32,103,21,15,47,57,1,5,32,20,57,63,15,1,48,51,80,23,8,18,1,16,1};

//建立哈夫曼树
void creattreehuffman(huftree tree[]) 
{
	int i, j, p1, p2;
	double least1, least2;
	for (i=1; i<=hufnum; i++)
	{
		tree[i].name = '\0';
		tree[i].parent = 0;
		tree[i].lchild = 0;
		tree[i].rchild = 0;
		tree[i].weight = 0.0;
	}
	for (i=1; i<=leafnum; i++)
	{
		tree[i].name = ch[i];
		tree[i].weight = w[i];
	}
	for (i=leafnum+1; i<=hufnum; i++)
	{
		p1=0; p2=0; least1=least2=maxdouble;
		for (j=1; j<i; j++)
			if (tree[j].parent==0)
				if (tree[j].weight<least1)
				{
					least2=least1;
					least1=tree[j].weight;
					p2=p1;
					p1=j;
				}
				else
				{
					if(tree[j].weight<least2)
					{
						least2=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;
	}
	tree[hufnum-1].parent=0;
}
// 建立哈夫曼编码
void creatcodehuffman(hufcode code[], huftree tree[])   
{
	int i, c, p;
	hufcode buf;
	for (i=1; i<=leafnum; i++)
	{
		buf.ch=ch[i];
		buf.start=leafnum;
		c=i;
		p=tree[i].parent;
		while (p != 0)
		{
			buf.start--;
			if (tree[p].lchild==c)
				buf.bits[buf.start]='0';
			else buf.bits[buf.start]='1';
			c=p;
			p=tree[p].parent;
		}
		code[i]=buf;
	}
}
//哈夫曼编码
void writecodehuffman(hufcode code[], huftree tree[])
{
	int i, j, k, n=0;
	char c[100];
	printf("请输入字符串:\n");
	gets(c);
	printf("\n");
	printf("则字符串的哈夫曼编码为:\n");
	for(i=0;i<strlen(c);i++)
	{
		for (j=1; j<=leafnum; j++)
			if (c[i]==tree[j].name)
				for(k=code[j].start; k<leafnum; k++)
					{
						printf("%c", code[j].bits[k]);
						huffmancode[n] = code[j].bits[k];
						n++;
					}
	}
} 
//哈夫曼译码
void transcodehuffman(hufcode code[], huftree tree[], char s[]) 
{
	int i;
	char *q=NULL;
	i=hufnum-1; q=s;
	while (*q!='\0')
	{
		if (*q=='0') i=tree[i].lchild;
		if (*q=='1') i=tree[i].rchild;
		if ((tree[i].lchild==0)&&(tree[i].rchild==0))
		{
			printf("%c",code[i].ch);
			i = hufnum - 1;
		}
		q++;
	}
	printf("\n");
}
//输出哈夫曼树
void printtreehuffman(huftree tree[])    
{
	int i;
	printf("根据字符的使用概率所建立的哈夫曼数为:\n");
	printf( "字符序号   字符名称      字符频率    双亲位置    左孩子  右孩子\n");
	for (i = 1; i < hufnum; i++)
	{
		printf("\t %d \t %c \t ",i,tree[i].name);
		printf(" %f \t %d \t %d \t %d \n", tree[i].weight,tree[i].parent,tree[i].lchild,tree[i].rchild) ;	
	}
}
//输出每个字符的哈夫曼编码
void printcodehuffman(hufcode code[])                
{
	int i, j;
	printf( "根据哈夫曼树对字符所建立的哈夫曼编码为:\n");
	printf( "字符序号   字符名称   字符编码 \n");
	for (i =1; i <= leafnum; i++)
	{
		printf("\t %d \t %c ",i,code[i].ch);
		for(j=code[i].start;j <leafnum;j++)
			printf("%c",code[i].bits[j]);
		printf("\t \t \n");
	}
}
//主函数
void main()
{
	FILE *fp;
	int i;
	char buffer[1000];
	creattreehuffman(tree);
	printtreehuffman(tree);
	creatcodehuffman(code, tree);
	printcodehuffman(code);
	writecodehuffman(code, tree);
	if((fp=fopen("e:\\codefile.txt","w"))==NULL)
	{
		printf("文件打开失败!\n");
		exit(0);
	}
	fprintf(fp,"哈夫曼编码为: %s",huffmancode);

	for(i=0;i<strlen(huffmancode);i++)
	{
		buffer[i] = huffmancode[i];
	}
	printf("\n");
	printf( "codefile文件中的代码如下:\n" );
	printf( "哈夫曼编码为: %s \n\n",huffmancode );
	printf( "codefile文件中代码译码为:" );
	transcodehuffman(code, tree, buffer);
}

最后:编写实现方式有多种,小白欢迎大家批评指正
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值