赫夫曼树 | 实战演练

Source

🎈 作者:Linux猿

🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊!

🎈 关注专栏: 数据结构和算法成神路【精讲】优质好文持续更新中……🚀🚀🚀

🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬


目录

一、题目描述

二、输入描述

三、输出描述

四、测试样例

五、解题思路

六、代码实现

七、时间复杂度


本文是针对【数据结构和算法】超详细,超多图解,赫夫曼树详解 中实战演练题目的讲解,下面来详细讲解 POJ Entropy。

一、题目描述

原文题目较长,这里总结如下:

熵编码器是一种数据编码方法,通过对删除了“浪费”或“额外”信息的消息进行编码来实现无损数据压缩。有两种编码方式:

(1)固定长度编码:用 ASCII 编码的英文文本具有高度的熵,因为所有字符都使用相同的位数(8位)进行编码。

(2)用赫(哈)夫曼编码形式编码。

例如:考虑文本“AAAAA BCD”。使用ASCII编码,需要64位(每个字符 8 位,8个字符,8 * 8 = 64)。哈夫曼编码为将“A”与“0”编码,将“B”与“10”编码,“C”与“110”编码,以及将“D”与“111”编码。那么,字符串 “AAAAABCD” 需要 13 位,编码为“0000010110111”。压缩比为 4.9 : 1。

二、输入描述

输入文件将包含文本字符串列表,每行一个。文本字符串将仅包含大写字母数字字符和下划线(用于代替空格)。

输入的结束将包含一行,该行仅包含单词 “END” 作为文本字符串,此行不处理。

三、输出描述

对于输入中的每个文本字符串,输出 8 位 ASCII 编码的位长度、最佳无前缀可变长度编码的位长以及精确到小数点一位的压缩比。

四、测试样例

输入:

AAAAABCD
THE_CAT_IN_THE_HAT
END

输出:

64 13 4.9
144 51 2.8

样例一:字符串 “AAAAABCD” 通过 8 位 ASCII 编码长度为 64 位 (8 * 8 = 64),最佳无前缀可变长度编码(哈夫曼编码)为 13 位,压缩比为 64/13 约等于 4.9。

样例二:字符串 “THE_CAT_IN_THE_HAT” 通过 8 位 ASCII 编码长度为 144 位 (18 * 8 = 144),最贱无前缀可变长度编码(哈夫曼编码)为 51 位,压缩比 144/51 = 2.8。

五、解题思路

本题是赫(哈)夫曼编码的简单应用,本题首先需要计算出 8 位 ASCII 编码需要的位数,需要位数 = 字符串长度 * 8。

然后,就是赫(哈)夫曼树的应用了,步骤如下所示:

(1)统计每个单词出现的频率,可以先排序一下,然后遍历一遍字符串统计;

(2)模拟赫(哈)夫曼树建立过程,统计编码需要的位数。

六、代码实现

代码实现如下所示。

#include<iostream>
#include<queue>
#include<string>
#include<iomanip>
#include<algorithm>
using namespace std;
int main()
{
	string str;
	// C++ 优先队列
	priority_queue< int,vector<int>,greater<int> > Q;
	while(cin>>str && str != "END") {
		sort(str.begin(), str.end()); //排序后,相同字符相邻

		//统计每个字符出现的频次,并放入优先队列
		int num = 1;
		int n = str.size();
		for(int i = 0; i < n; ++i) {
			if(str[i] != str[i + 1]) {
				Q.push(num);
				num = 1;
			} else {
				++num;
			}
		}

		int ans = 0; // 用于保存哈夫曼编码总长度
        // 处理只有一种字符的特殊情况
		if(Q.size() == 1) ans = Q.top();
		while(Q.size() > 1) {
            // 模拟建立哈夫曼树,取出两个最小的
			int a = Q.top();
			Q.pop();
			int b = Q.top();
			Q.pop();
			Q.push(a + b);
			ans += (a + b);
		}
		Q.pop();
		cout<<n*8<<" "<<ans<<" "<<fixed<<setprecision(1)<<n*8.0/ans<<endl;
	}
	return 0;
}

在 POJ 提交 AC 截图如下所示。

AC 截图

七、时间复杂度

时间复杂度:O(nlogn + mlogm)

其中,n 表示字符的个数,m 表示字符的种类,对所有字符串排序的时间复杂度为 O(nlogn),通过优先队列进行赫(哈)夫曼编码的时间复杂度为O(mlogm)。


🎈 感觉有帮助记得「一键三连支持下哦!有问题可在评论区留言💬,感谢大家的一路支持!🤞猿哥将持续输出「优质文章回馈大家!🤞🌹🌹🌹🌹🌹🌹🤞