C++哈夫曼编码

题目描述:

给定一个文本中n个字符的出现频率。设计n个字符的哈夫曼编码。

输入描述:

输入包括多组数据。每组数据的第一行是一个整数n(1 <= n <= 100),代表不同字符的总数,n = 0意味着输入结束;接下来有n行,其中的每一行包含一个字符及其出现频率。

输出描述:

对每组数据,输出哈夫曼编码的平均位数,即l1 * f1 + l2 * f2 + ... + ln * fn,其中li是第i个字符的哈夫曼编码的位数,fi是第i个字符出现的频率。

样例输入:

5
A 35
B 10
C 20
D 20
_ 15
3
x 20
Y 50
Z 5
0

上代码:

#include<iostream>
#include<string>
 
using namespace std;
int n;
//定义一个结构体,用作结点信息存储
struct ElemType {
	string code="";		//用来存储该字符的哈夫曼编码
	char data;		//用来存储叶子节点的字符
	int weight;		//假设权值为整数,用来存储叶子结点的权重
	int parent, lchild, rchild;		//用来存储父节点,以及左右孩子结点
	int index = -1;//用来存储原来结构体数组下标,以便于后面给i1,i2赋值
	int LR;	//用来唯一标记该结点是左孩子还是右孩子
};
 
void Select(ElemType huffTree[], int &i1, int &i2) {		//选择排序,以便于通过每个根节点的权重来获取两个权重最小的根节点下标志
	int count = 0;	//用来计数,方便后边给i1和i2赋值
	ElemType reserve[2 * n - 1];
	for (int i = 0; i < 2 * n - 1; i++) {		//将结构体数组的元素下标初始化
		huffTree[i].index = i;	//将结构体里面的index给初始化,防止排序过后顺序变化而导致获取的原来下标不是之前的数组小标
		reserve[i] = huffTree[i];//同时将reserve[i]用来暂时保存传入的哈夫曼数,以便于后边将这一个给赋值回来,
	}
	for (int i = 0; i < 2 * n - 1; i++) {		//使用冒泡排序对数组通过权重进行从小到大排序
		for (int j = 0; j < 2 * n - 1 - i; j++) {
			if (huffTree[j].weight > huffTree[j + 1].weight) {
				ElemType temp = huffTree[j + 1];
				huffTree[j + 1] = huffTree[j];
				huffTree[j] = temp;
			}
		}
	}
	for (int i = 0; i < 2 * n - 1; i++) {	//遍历huffTree数组,当排好了序的数组遍历到第一个结构体的parent=-1时候将其赋值给i1,之后小中断此循环,之后在进行相似操作,以获取i2
		if (count == 0 && huffTree[i].parent == -1) {		//定义count方便用来获取权重最小的和倒数第二小的结点
			i1 = huffTree[i].index;
			count = 1;
			continue;	//找到第一个之后暂时终止程序,以防两个最小的被赋值为同一个下标
		}
		if (count == 1 && huffTree[i].parent == -1) {
			i2 = huffTree[i].index;
			break;		//找到下标后就可以中断
		}
	}
	//排序过后将haffmanTree恢复原来顺序
	for (int i = 0; i < 2*n-1; i++) {
		huffTree[i] = reserve[i];
	}
}
void createHuffmanTree(ElemType huffTree[], int w[], char ch[]) {
	int i, k, i1, i2;//使用i1和i2用来存储权值权值最小的两个根节点
	for (i = 0; i < 2 * n - 1; i++) {	//刚开始所有结点设置为没有双亲和孩子,由于有n个叶子结点就有2*n-1个节点,所以创建创建2*n-1个结点用来存储
		huffTree[i].parent = -1;//指向-1表示没有指向任何一个结点
		huffTree[i].lchild = huffTree[i].rchild = -1;
		huffTree[i].weight = 0x7fffffff;	//权重赋初值为无穷大,以便于后边排序从而寻找i1和i2
		huffTree[i].data = '\0';//给所有的结构体的字符赋值为空字符
	}
	for (i = 0; i < n; i++) {	//储存叶子结点的权值和对应的字符
		huffTree[i].weight = w[i];
		huffTree[i].data = ch[i];
	}
	for (k = n; k < 2 * n - 1; k++) {		//进行n-1次合并,至于为什么是n-1次,这是由于每一次合并都会产生一个新的结点,而除了叶子结点外所有的结点(n-1个)全是合并而来,所以就要进行n-1次合并
		Select(huffTree, i1, i2);		//权值最小的根节点下标为i1和i2
		huffTree[k].weight = huffTree[i1].weight + huffTree[i2].weight;	//新建立的结点权重就是两个子节点权重之和
		huffTree[i1].parent = huffTree[i2].parent = k;		//设置父索引是k
		huffTree[k].lchild = i1; huffTree[k].rchild = i2;	//设置左子索引是i1,右子索引是i2
		huffTree[i1].LR = 0; huffTree[i2].LR = 1;		//用来唯一的表示是左孩子还是右孩子
	}
}
  
//根据权重来实现哈夫曼编码
void EnHuffmanCode(ElemType huffTree[]) {
	for (int i = 0; i < n; i++) {	//有n个叶子结点,就要进行n次编码,左子树编码为0,右子树编码为1的规则
		int j = i;		//利用j来存储i以便于后面不至于改变i的值
		while (huffTree[i].parent != -1) {		//循环至根节点停止
			if (huffTree[i].LR == 0) {	//如果该叶子结点的索引等于他父节点的左孩子的索引,也就是判断该结点是他父节点的左孩子,就给编码前面加上"0"
				huffTree[j].code = "0" + huffTree[j].code;		//如果是左孩子就在前面添加上"0"字符
				i = huffTree[i].parent;		//将i用父节点的下标代替
			}
			if (huffTree[i].LR == 1) {	//如果该叶子结点的索引等于他父节点的右孩子的索引,也就是判断该结点是他父节点的右孩子,就给编码前面加上"1"
				huffTree[j].code = "1" + huffTree[j].code;
				i = huffTree[i].parent;		//将i用父节点的下标代替
			}
		}
		i = j;	//将保存了i的j赋值给i
	}
}
 
void printCode(ElemType huffTree[]) {
	int s = 0; 
	for (int i = 0; i < n; i++) {
		int len = huffTree[i].code.length(); 
		
		s+=len*huffTree[i].weight;
	}
	cout<<s<<endl;
	
}
 
int main() {
		while(cin>>n&&n!=0){  //以0为结束标志 
			char ch[n];  int w[n]; 
			for(int i=0;i<n;i++){
				cin>>ch[i]>>w[i];  //输入每个字符,以及对应的权重 
			}
				ElemType huffTree[2 * n];
				createHuffmanTree(huffTree, w, ch);
				EnHuffmanCode(huffTree);
				printCode(huffTree);	 
		}
	
	return 0;
}

输出

225
100

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈夫曼编码是一种可变长度编码的压缩算法,通过根据字符出现的频率构建最优前缀编码表来减少数据的存储空间。 首先,根据字符的频率构建哈夫曼树。哈夫曼树是一种特殊的二叉树,其每个叶子节点代表一个字符,而每个非叶子节点代表出现频率较低的两个字符的合并。频率越高的字符距离根节点越近,频率越低的字符距离根节点越远。 然后,根据哈夫曼树构建编码表。从根节点开始,向左路径表示编码0,向右路径表示编码1。将每个叶子节点的路径编码存储在编码表中,以便后续的编码和解码过程中使用。 编码过程中,将要压缩的数据按照编码表进行替换。将原始的字符序列转换为哈夫曼编码序列,从而减少存储空间。编码后的数据长度可能会比原始数据短。 解码过程中,根据编码表将哈夫曼编码序列转换为原始字符序列。从根节点开始,根据编码(0或1)判断是向左还是向右,直到叶子节点找到对应的字符。解码后的数据与原始数据保持一致。 哈夫曼编码十分有效,特别适用于频率分布不均的数据。频率高的字符可以使用较短的编码,频率低的字符可以使用较长的编码,整体上减少了数据的存储空间。但是,在编码和解码过程中,需要使用编码表进行对应,因此需要将编码表存储在文件中或者以其他方式传输。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值