哈夫曼编码与译码

哈夫曼编码与译码实现详解
本文介绍如何对字符集{A,C,I,M,N,P,T,U}进行哈夫曼编码和译码,详细解析哈夫曼树的构建过程,以及C++实现中的关键步骤,包括使用priority_queue和自定义比较函数。同时提供了一个样例,展示编码和译码的实际应用。

哈夫曼编码与译码

时间限制(普通/Java):1000MS/3000MS          运行内存限制:65536KByte
总提交:386            测试通过:141

描述

已知电文包括的字符集为{ACIMNPTU},输入对应权值,对字符集合进行哈夫曼编码,完成电文的哈夫曼编码与译码工作。

输入

共三行:

第一行为对应字符集{ACIMNPTU}的权值

第二行为一段字符串表示的电文(长度不超过1000);

第三行为一段电文的哈夫曼编码。

输出

共十行:

前八行为各字符的编码;

第九行是与第二行输入对应的哈夫曼编码;

第十行是与第三行输入对应的电文。

样例输入

1 2 3 4 5 6 7 8
NUPTICPCACM
1111011111100

样例输出

A: 11110
C: 11111
I: 1110
M: 100
N: 101
P: 110
T: 00
U: 01
1010111000111011111110111111111011111100
ACM

提示

2012.4.11 更新

题目来源

NUPT ACM



分析:以前只了解哈夫曼树的原理,没想到实现起来遇到了不少难题。首先要创建一个哈夫曼树节点类,包含权重(weight)、英文字符(data),内部定义了构造函数。链表实现左孩子和右孩子。

创建哈夫曼树原理:取权值最小的2个,合并生成一个新的节点,新的节点的权值为2个孩子的权值的和。重复直到只有一个根节点。

C++实现运用priority_queue,并且重写了compare方法(选取权值最小的),priority_queue用了top、pop、push函数。然后也是很关键的编码,编码利用递归将英文字符的编码保存在二维数组int code[x][1]~code[x][m]中,x代表字符的ASCII码,m代表该英文字符的编码的位数,以此来区分,利用了int tmp[]数组存放临时的0/1。

编码原理:左边为0,右边为1。

输出即为code[ str[i] ][j]

译码逐个分析输入的字符串,并且利用建立的哈夫曼树,从哈夫曼树的根节点开始,输入为1指向右孩子,输入位0指向左孩子。然后判断该节点的英文字符(data)是否为'0',因为只有叶子节点的data为('A'/'C'/'I'/'M'...),而合并的节点初始化data为字符'0'。以此来判断是否出现了匹配的英文字符。别忘了队列要重置p = myQueue.top();

还是有细节问题。关于哈夫曼树写法也有很多。

#include<iostream>
#include<queue>
#include<string>
using namespace std;
#define MAX 10

//哈夫曼编码与译码

char str[8] = {'A','C','I','M','N','P','T','U'};
int tmp[MAX*3];
int code[256][MAX*3]; // code[A][0]~code[A][m] = 100111...'A'——ASCII码
string strInput;

class HTNode
{
public:
	int weight; // 权重 1/2/3...
	char data; // A/C/I...
	HTNode *lChild, *rChild;

	HTNode()
	{
		data = '0';
		weight = 0;lChild = rChild = NULL;
	}
	HTNode(int a,char s)
	{
		weight = a; lChild=rChild = NULL;
		data = s;
	}
};

HTNode* uni(HTNode *p,HTNode *q) // p、q合并
{
	HTNode *tmp = new HTNode(p->weight + q->weight, '0');
	tmp->lChild = p;
	tmp->rChild = q;
	return tmp;
}

// 哈夫曼编码 递归
void bianma(HTNode *p,int m) 
{
	if(p->rChild == NULL)
	{
		code[p->data][0] = m; // 编码为多少位
		for(int i=1;i<=m;i++)
		{
			code[p->data][i] = tmp[i-1];
		}
	}
	else
	{
		tmp[m] = 0;
		bianma(p->lChild,m+1); // 左0
		tmp[m] = 1;
		bianma(p->rChild,m+1); // 右1
	}
}
//用于实现优先级的比较函数 
class HTNodeCmp
{
public:
	bool operator()(HTNode* p1, HTNode* p2) const
	{
		return p1->weight > p2->weight;
	}
};

/*
//层次遍历 输出节点的权值
void LevelOrder(HTNode *&t)
{
	int front = 0, rear = 1; 
	HTNode *p[100];
	p[0] = t;
	while(front < rear)
	{
		if(p[front])
		{
			cout<<" "<<p[front]->weight; 
			p[rear++] = p[front]->lChild;
			p[rear++] = p[front]->rChild;
			front ++;
		}
		else
			front ++;
	}
}
*/

priority_queue<HTNode *, deque<HTNode *>, HTNodeCmp> myQueue;

int main()
{
	int i,a;
	for(i=0;i<8;i++)
	{
		cin>>a;
		HTNode *p;
		p = new HTNode(a, str[i]); // 自动构造 8个节点 
		myQueue.push(p); // 加入一个元素
	}
	while(myQueue.size()>1) // >1,只剩根节点
	{
		HTNode *p, *q, *k;
		p = myQueue.top(); // 权值最小的
		//cout<<p->weight<<" ";
		myQueue.pop(); // 调用pop删除这个元素

		q = myQueue.top(); // 权值最小的
		//cout<<q->weight<<endl;
		myQueue.pop();
		k = uni(p, q); // key!
		myQueue.push(k);
	}
	HTNode *p = myQueue.top(); // 权值最大的
	bianma(p, 0); // 编码

	for(i=0;i<8;i++)
	{
		cout<<str[i]<<": ";
		for(int j=1;j<=code[str[i]][0];j++)
		{
			cout<<code[ str[i] ][j];
		}
		cout<<endl;
	}
	//LevelOrder(p);

	cin>>strInput;
	int len = strInput.length();
	for(i=0;i<len;i++)
	{
		for(int j=1;j<=code[strInput[i]][0];j++)
		{
			cout<<code[strInput[i]][j];
		}
	}
	cout<<endl;

	//译码
	cin>>strInput;
	len = strInput.length();
	HTNode *q = myQueue.top();
	for(i=0;i<len;i++)
	{

		if(strInput[i] == '1')
		{
			q = q->rChild;
		}
		else
		{
			q = q->lChild;
		}
		if(q->data != '0') // 找到对应的字母
		{
			cout<<q->data;
			q = myQueue.top(); // 重置q 队列
		}
	}
	cout<<endl;

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值