数据结构课设

Huffman编/译码器

【问题描述】

利用Huffman编码进行通信可以大大提高信道利用率.缩短信息传输时间,降低传输成本,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个Huffman码的编/译码系统。

【基本要求】

一个完整的系统应具有以下功能:

(l)I:初始化。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。

(2)E:编码。利用已建好的Huffman树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。

(3)D:译码。利用已建好的Huffman树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。

(4)P:印代码文件。将文件CodeFile以紧凑格式显示在终端上,每行50 个代码。

(5)T:印哈夫曼树。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。

【输入输出】

(l)利用教材中的数据或自行选择一份英文文本材料调试程序。

(2)用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:"THIS PROGRAM IS MY FAVORITE"。 

字符

  

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

频度

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

【实现提示】

(1)文件CodeFile的基类型可以设为子界型bit=0..1。

(2)用户界面可以设计为“菜单”方式:显示上述功能符号,再加上“Q”,表示退出运行Quit。请用户键入一个选择功能符。此功能执行完毕后再显示此菜单,直至某次用户选择了“Q”为止。

(3)在程序的一次执行过程中,第一次执行I,D或C命令之后,Huffman树已经在内存了,不必再读入。每次执行中不一定执行I命令,因为文件hfmTree可能早已建好。

【选做内容】

(1)上述CodeFile的基类型实际上可能占用了存放一个整数的空间,只起到示意或模拟的作用。现使CodeFile的基类型package=integer,把Huffman码紧缩到一个整形变量中去,最大限度地利用码点存储能力,试改写此系统。

(2)修改系统,实现对系统的原程序的编码和译码(主要是行尾符编/译码问题)。

(3)实现各个转换操作的源/目文件均由用户在选择此操作时指定。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<math.h>
typedef struct {
	int weight;
	char ch;
	int parent, lchild, rchild;
}HTNode, * HuffmanTree;//动态分配数组存储哈夫曼树


typedef char** HuffmanCode; //存储哈夫曼编码

//定义全局变量
HuffmanTree HT;
int n = 0;
HuffmanCode HC;
int* w;
char* ch;

void welcome();    //操作选择界面
void select(HuffmanTree HT, int j, int& s1, int& s2); //选择parent为0且weight最小的两个结点
void Init(); //输入字符及其权值,建立哈夫曼树
void Encoding(); //编码
void Decoding(); //译码
void Tree_printing(); //打印哈夫曼树 
void Print();//打印代码 
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n, char* ch);
void Free();//释放动态分配空间 

void welcome()
{
	printf("**************************************\n");
	printf("*          请选择如下操作            *\n");
	printf("*            I.初始化                *\n");
	printf("*            E.编码                  *\n");
	printf("*            D.译码                  *\n");
	printf("*            P.印代码文件            *\n");
	printf("*            T.打印哈夫曼树          *\n");
	printf("*            Q.退出                  *\n");
	printf("**************************************\n");
}

//输入字符及其权值,建立哈夫曼树
void Init()
{
	FILE* fp;
	int i;
	printf("请输入字符个数:\n");
	scanf("%d", &n);
	while (n < 2) {
		printf("输入的数据有误,请重新输入:\n");
		scanf("%d", &n);
	}
	ch = (char*)malloc(n * sizeof(char));
	printf("请输入所有字符:\n");
	getchar();
	for (i = 0; i < n; ++i)
		scanf("%c", &ch[i]);
	w = (int*)malloc(n * sizeof(int));
	printf("请输入这些字符的权值:\n");
	for (i = 0; i < n; ++i)
		scanf("%d", &w[i]);
	HuffmanCoding(HT, HC, w, n, ch);
	/*printf("编码结果如下:\n");
	for(i=1;i<=n;++i)
		printf("%c:%s\n",HT[i].ch,HC[i]);*///调试 
	fp = fopen("hfmTree.txt", "w");
	fprintf(fp, "%d", n);
	for (i = 0; i < n; ++i)
		fprintf(fp, "%c", ch[i]);
	for (i = 0; i < n; ++i)
		fprintf(fp, "%5d", w[i]);
	for (i = 1; i <= n; ++i)
		fprintf(fp, "\n%c:%s", HT[i].ch, HC[i]);
	fclose(fp);
	printf("初始化成功\n");
}

//选择parent为0且weight最小的两个结点
void select(HuffmanTree HT, int j, int& s1, int& s2)
{
	int i;
	s1 = -1;
	for (i = 1; i <= j; i++)
		if (HT[i].parent == 0) {
			if ((s1 == -1) || (HT[i].weight < HT[s1].weight)) {
				s1 = i;
			}
			HT[s1].parent = 1;

			s2 = -1;
			for (i = 1; i <= j; i++)
				if (HT[i].parent == 0) {
					if ((s2 == -1) || (HT[i].weight < HT[s2].weight)) {
						s2 = i;
					}
					HT[s2].parent = 1;
				}

		}
}


void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n, char* ch){
	int m;
	m = 2 * n - 1;//一共2n-1个节点
	int s1, s2;
	HT = (HTNode*)malloc((m + 1) * sizeof(HTNode));//开辟节点空间
	HTNode* p;
	int i;
	for (p = HT + 1, i = 1; i <= n; ++p, ++i, ++w, ++ch) {
		p->ch = *ch;
		p->weight = *w;
		p->parent = p->lchild = p->rchild = 0;//向节点中填入字符,权值,以及双亲,孩子的标识
	}
	for (; i <= m; ++i, ++p) {
		p->ch = 0;
		p->weight = 0;
		p->parent = p->lchild = p->rchild = 0;//初始化第n个以后的节点
	}
	for (i = n + 1; i <= m; ++i) {//从第n+1个开始
		select(HT, i - 1, s1, s2);//从前n个中找具有最小的权值的两个字符,i作为其双亲
		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;
	}
	HC = (char**)malloc((n + 1) * sizeof(char*));//哈夫曼码
	char* cd;
	cd = (char*)malloc(n * sizeof(char));
	cd[n - 1] = '\0';
	int total = 0;
	int c, f;
	for (i = 1; i <= n; ++i) {
		for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent) {
			if (HT[f].lchild == c) { cd[total++] = '0'; }
			else cd[total++] = '1';
		}
		HC[i] = (char*)malloc((total) * sizeof(char));

		int j;
		for (j = total - 1; j >= 0; j--) HC[i][total - 1 - j] = cd[j];
	}
	free(cd);
}

//编码
void Encoding()
{
	FILE* fp, * fp1;
	int i;
	char in;
	if (n == 0)
	{
		fp = fopen("hfmTree.txt", "r");
		fscanf(fp, "%d", &n);
		if (feof(fp)) {
			printf("请先初始化哈夫曼树!\n");
		}
		ch = (char*)malloc(n * sizeof(char));
		for (i = 0; i < n; ++i)
			fscanf(fp, "%c", &ch[i]);
		w = (int*)malloc(n * sizeof(int));
		for (i = 0; i < n; ++i)
			fscanf(fp, "%d", &w[i]);
		HuffmanCoding(HT, HC, w, n, ch);
		printf("已载入哈夫曼树!\n");
		fclose(fp);
	}
	fp = fopen("ToBeTran.txt", "r");
	printf("成功读取ToBeTran.txt\n");
	fp1 = fopen("CodeFile.txt", "w");
	fscanf(fp, "%c", &in);//读取一个字符
	while (!feof(fp))
	{
		for (i = 1; i <= n; ++i) {
			if (HT[i].ch == in)//循环直至找到目标字符所在位置
				break;
		}

		fprintf(fp1, "%s", HC[i]);//把哈夫曼码写入文件
		fscanf(fp, "%c", &in);//再读取一个字符
	}
	printf("\n编码结束,结果已存入CodeFile.txt文件中!\n");
	fclose(fp);
	fclose(fp1);
}

//译码
void Decoding()
{
	FILE* fp, * fp1;
	int i, m;
	char in;
	if (n == 0)
	{
		fp = fopen("hfmTree.txt", "r");
		fscanf(fp, "%d", &n);
		if (feof(fp)) {
			printf("请先初始化哈夫曼树!\n");
		}
		ch = (char*)malloc(n * sizeof(char));
		for (i = 0; i < n; ++i)
			fscanf(fp, "%c", &ch[i]);
		w = (int*)malloc(n * sizeof(int));
		for (i = 0; i < n; ++i)
			fscanf(fp, "%d", &w[i]);
		HuffmanCoding(HT, HC, w, n, ch);
		printf("已载入哈夫曼树!\n");
		fclose(fp);
	}
	fp = fopen("CodeFile.txt", "r");
	fp1 = fopen("TextFile.txt", "w");
	for (i = 1; HT[i].parent != 0; ++i);//找到根节点
	m = i;//根节点编号赋值给m
	while (!feof(fp)) {
		if (HT[m].lchild && HT[m].rchild) {
			fscanf(fp, "%c", &in);//读取被编码的文件的1位
			if (in == '0') m = HT[m].lchild;//如果为0,则把此节点的左孩子赋值给m,直至终端节点
			else m = HT[m].rchild;
		}
		else {
			fprintf(fp1, "%c", HT[m].ch);//把解码出来的字符写入文件
			m = i;
		}
	}
	printf("\n译码结束,译码结果已保存到TextFile.txt文件中!\n");
	fclose(fp);
	fclose(fp1);
}

// 打印代码
void Print()
{
	FILE* fp;
	int i = 0;
	char in;
	fp = fopen("CodeFile.txt", "r");
	//fp1 = fopen("CodePrin.txt", "w");
	fscanf(fp, "%c", &in);
	while (!feof(fp))
	printf("代码文件为:\n");
	{
		printf("%c", in);
		//fprintf(fp1, "%c", in);
		i++;
		if (i == 50) {
			printf("\n");
			//fprintf(fp1, "\n");
			i = 0;
		}
		fscanf(fp, "%c", &in);
	}
	printf("\n");
	fclose(fp);
	//fclose(fp1);
	//printf("代码文件已存入CodePrin.txt文件中");
	printf("\n");
}

//打印哈夫曼树 
void Tree_printing()
{
	FILE* fp;
	fp = fopen("TreePrint.txt", "w");
	int i;
	printf("字符  权值    双亲    左孩子  右孩子\n");
	for (i = 1; i < 2 * n; i++) {
		printf("%c%8d%8d%8d%8d\n", HT[i].ch, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
		fprintf(fp, "%c%8d%8d%8d%8d", HT[i].ch, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
		fprintf(fp, "\n");
	}
	fclose(fp);
	printf("哈夫曼树已存入TreePrint.txt\n");
}

//释放动态分配空间 
void Free()
{
	free(HT);
	free(HC);
	free(w);
	free(ch);
}


int main()
{
	char choice;
	while (1)
	{
		welcome();
		scanf("%c", &choice);
		switch (choice)
		{
		case 'i':
		case 'I':Init(); break;
		case 'e':
		case 'E':Encoding(); break;
		case 'd':
		case 'D':Decoding(); break;
		case 'p':
		case 'P':Print(); break;
		case 't':
		case 'T':Tree_printing(); break;
		case 'q':
		case 'Q':Free(); exit(1);
		default:printf("Input error!\n");
		}
		getchar();
	}
	return 0;
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值