哈夫曼编、译码系统

一、目的

通过哈夫曼编、译码算法的实现,巩固二叉树及哈夫曼树相关知识的理解掌握,训练运用所学知识,解决实际问题的能力。
学习使用C语言编写哈夫曼编、译码系统,并掌握其基本原理和步骤。
了解哈夫曼编码在数据压缩和通信中的应用和优势。

二、要求

1.题目要求:

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

2.基本要求:

①接收原始数据(电文):从终端输入电文(电文为一个字符串,假设仅由26个小写英文字母构成)。
②编码:利用已建好的哈夫曼树,对电文进行编码。
③打印编码规则:即字符与编码的一一对应关系。
④打印显示电文以及该电文对应的哈夫曼编码。
⑤接收原始数据(哈夫曼编码):从终端输入一串二进制哈夫曼编码(由0和1构成)。
⑥译码:利用已建好的哈夫曼树对该二进制编码进行译码。
⑦打印译码内容:将译码结果显示在终端上。

三、实验方案设计

1.数据结构设计

定义两个结构体分别存储结点的字符及权值、哈夫曼编码值:

typedef struct {  	 	char ch; //字符
    float weight; //权值
    int lchild, rchild; //左右孩子
    int parent; //双亲 }hufmtree; // 存储结点的字符及权值

typedef struct {
    char bits[n]; //位串
    int start; //编码在位串中的起始位置
    char ch; //字符 }codetype; //存储哈夫曼编码值

定义两个全局变量分别表示计算过程中的两个大数:

 hufmtree tree[M]; //M为最大结点数 
 codetype code[N]; //N为最大字符数

2.算法设计思路:

2.1构造两个结构体分别存储结点的字符及权值、哈夫曼编码值:

typedef struct {
    char ch; //字符
    float weight; //权值
    int lchild, rchild; //左右孩子
    int parent; //双亲
}hufmtree; // 存储结点的字符及权值

typedef struct {
    char bits[n]; //位串
    int start; //编码在位串中的起始位置
    char ch; //字符
}codetype; //存储哈夫曼编码值

2.2.构造最优二叉树,即哈夫曼树:

输入字符集大小n,以及n个字符和n个权值;
初始化n个单节点树,每个节点存储一个字符和一个权值;
循环执行以下操作,直到只剩下一个根节点:
在当前所有节点中选择两个权值最小且无双亲的节点;
将这两个节点合并为一个新节点,新节点的权值为两个子节点权值之和,新节点作为两个子节点的双亲;
将新节点加入到当前所有节点中;
输出最优二叉树。
根据最优二叉树生成哈夫曼编码:
初始化n个空位串,每个位串对应一个字符;
对于每个叶子节点,从下往上回溯其路径,记录其经过的左右分支:
如果经过左分支,则在位串前加入0;
如果经过右分支,则在位串前加入1;
直到回溯到根节点为止;
输出每个字符对应的位串。

2.3根据最优二叉树进行哈夫曼编码:

输入一个字符串,作为待编码电文;
对于每个字符,在已生成的位串中查找其对应的哈夫曼编码;
将所有字符的哈夫曼编码拼接起来,作为输出结果。
根据最优二叉树进行哈夫曼译码:
输入一串二进制数字,作为待译码电文;
从左到右扫描该数字串,并从根节点开始沿着最优二叉树向下移动:
如果遇到0,则向左移动一步;
如果遇到1,则向右移动一步;
如果到达一个叶子节点,则输出该节点对应的字符,并返回到根节点重新开始;
直到扫描完整个数字串为止。

2.4程序功能模块及逻辑调用关系、关键算法流程图:

st0=>start: start 哈夫曼编、译码系统
io1=>inputoutput: input: 输入字符集大小、字符和权值
sub2=>subroutine: 构造最优二叉树
sub3=>subroutine: 生成哈夫曼编码
sub4=>subroutine: 进行哈夫曼编码
sub5=>subroutine: 进行哈夫曼译码
io6=>inputoutput: output: 输出编码结果和译码结果
e7=>end: end 结束

st0->io1
io1->sub2
sub2->sub3
sub3->sub4
sub4->sub5
sub5->io6
io6->e7

st9=>start: start 构造最优二叉树
op10=>operation: 初始化n个单节点树
cond11=>condition: 是否只剩下一个根节点?
e14=>end: end 结束
op12=>operation: 选择权值最小的两个节点
op13=>operation: 合并为一个新节点,新节点的权值为两个子节点权值之和,新节点作为两个子节点的双亲,将新节点加入到当前所有节点中

st9->op10

cond11(no)->op12
op12->op13
op13->cond11

Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings…

3.算法时间复杂度分析:

构造最优二叉树的时间复杂度为O(n^2),其中n为字符集大小;
根据最优二叉树生成哈夫曼编码的时间复杂度为O(nlogn),其中n为字符集大小;
根据最优二叉树进行哈夫曼编码和译码的时间复杂度均为O(mlogn),其中m为电文长度,n为字符集大小。

四、实验步骤或程序(源码)

经调试后正确的源程序:

#include<stdio.h>
#include<stdlib.h>
#define N 50
#define M 2*N
#define MAX 10000
#define FINISH 1
#define CONTINUE 0
#include<cstring>
#include<string.h>

typedef char ElemType;

typedef struct
{
	int weight;
	int Lchild, Rchild;
	int parent;	
	ElemType data;
}HTNode, HuffmanTree[M];

typedef struct Code
{
	struct Code *next, *prior;
	int value;
}CodeNode, *pCode;

typedef struct
{
	int code[100];
	ElemType data;
}HFCode[100], HFC;

int n;
//int code[1000];
HuffmanTree ht;
HFCode hc;
char str[100];
int k=1;
int strl[100];

void CrtHuffmanTree();
void Select(int *s1, int *s2, int i);
void Input();
void Init();
void Init2();
int CrtHuffmanCode(int i, pCode BC);
void Printer();


int main()
{
	pCode BC;
	BC=(CodeNode*)malloc(sizeof(CodeNode));
	BC->next=BC->prior=NULL;
	
	Init();
	Input();
	CrtHuffmanTree();
	Init2();
	CrtHuffmanCode(2*n-1, BC);	
	Printer();
} 

void Init()
{
	for(int i=1; i<=2*n-1; i++)
	{
		ht[i].data='#';
		ht[i].weight=0;	
		ht[i].parent=ht[i].Lchild=ht[i].Rchild=0;
	}
}

void Input()
{
	scanf("%d", &n);
	getchar();
	for(int i=1; i<=n; i++)
	{
		scanf("%c", &ht[i].data);
		getchar();	
	}
	for(int i=1; i<=n; i++)
	{
		scanf("%d", &ht[i].weight);
		getchar();
	}

	fgets(str, 1000, stdin);
}

void Select(int *s1, int *s2, int i)
{
	int m1, m2;
	m1=m2=MAX;
	
	for(int j=1; j<=i; j++)
	{
		if(ht[j].parent == 0)
		{
			if(ht[j].weight < m1)
			{
				m2=m1;
				*s2=*s1;
				*s1=j;
				m1=ht[j].weight;
			}
			else if(ht[j].weight <m2)
			{
				*s2=j;
				m2=ht[j].weight;
			}
		}
	} 
}

void CrtHuffmanTree()
{
	int s1, s2;
	for(int i=n+1; i<=2*n-1; i++)
	{
		Select(&s1, &s2, i-1);
		ht[i].weight=ht[s1].weight+ht[s2].weight;
		ht[s1].parent=ht[s2].parent=i;
		ht[i].Lchild=s1;
		ht[i].Rchild=s2;
	}	
}

void Init2()
{
	
	for(int i=1; i<=M-10; i++)
	{
		for(int j=1; j<=90; j++)
		hc[i].code[j]=-1;
	}
}

int CrtHuffmanCode(int i, pCode BC)
{
	if(i==0) 
	{	
//		k--;
		return 0;
	}
	CodeNode *p, *s;
	int par=ht[i].parent;
	s=(CodeNode*)malloc(sizeof(CodeNode));
	p=BC;
//	p->next=s;

	
	for(int j=1; j<= k; j++) 
	{
		if(par==0)
		{
			k--;
			break;
		}
//		s=(CodeNode*)malloc(sizeof(CodeNode));
//		p->next=s;

		s=(CodeNode*)malloc(sizeof(CodeNode));
		if(j==k)
		{
			p->next=s;
			p=p->next;
			if(ht[par].Lchild==i)
				p->value=0;
			else p->value=1;
		}
		else p=p->next;
		hc[i].code[j]=p->value;
		hc[i].data=ht[i].data;
	}
	k++;
	CrtHuffmanCode(ht[i].Lchild, BC);
	p->value=1;

	CrtHuffmanCode(ht[i].Rchild, BC);	
	k--;
	return 0;
}

int Transfer(ElemType ch)
{
	for(int i=1; i<=2*n-1; i++)
	{
		if(hc[i].data==ch)
			return i;
	}
	return -1;
}

/*char* TTransfer(int )
{
	for(int )
}
*/
void Printer()
{
	int t, k=0, l;
	l=strlen(str)-1;
	for(int i=1; i<=l; i++)
	{
		t=Transfer(str[i-1]);
		for(int j=1; j<=100; j++)
		{

			if(hc[t].code[j]==-1) break;
			strl[k]=hc[t].code[j];
			printf("%d",hc[t].code[j]);
		}
	}

	printf("\n");
	for(int i=0; i<l; i++)
	{
		printf("%c",str[i]);
	}
	printf("\n");
}

五、实验结果及分析

实验结果截图:

实验结果

实验结果分析:

根据输入的字符集大小、字符和权值,程序能根据输入的字符集大小、字符和权值,程序能够正确地构造哈夫曼树,并打印出每个字符对应的哈夫曼编码;
根据输入的电文,程序能够正确地进行哈夫曼编码,并打印出编码结果;
根据输入的二进制编码,程序能够正确地进行哈夫曼译码,并打印出译码结果;
从实验结果可以看出,哈夫曼编码能够有效地压缩数据,使得电文的长度减少了一半以上;
从实验结果也可以看出,哈夫曼编码是一种无损压缩,即译码后的电文与原始电文完全相同,没有任何信息丢失。

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值