基于Huffman编码的字符串统计及WPL计算

一、问题描述

问题概括:
给定一个字符串或文件,基于Huffman编码方法,实现以下功能:

1.统计每个字符的频率

2.输出每个字符的Huffman编码

3.计算并输出WPL(加权路径长度)

这个问题要求对Huffman编码算法进行实现和扩展,具体涉及以下步骤:

1.从键盘输入或文件中读取字符串/内容。

2.统计每个字符的出现频率。

3.根据频率构建Huffman树。

4.为每个字符生成对应的Huffman编码。

5.计算WPL并输出。

此外,还要求能够处理文件输入,这涉及到文件读取和解析的逻辑。解决此问题需要熟练掌握Huffman编码算法,包括如何构建Huffman树如何为字符生成编码以及如何计算WPL。同时,还需要具备基本的文件操作能力,如打开读取解析文件内容


二、分析与设计

(1)设计思想:

数据结构

  1. 二叉树:在这段代码中,我们使用了二叉树作为主要的数据结构。每个节点包含一个字符(数据)、一个频率(权重)以及指向左右子节点的指针。这种数据结构使我们能够有效地进行各种操作,如创建树、生成编码和计算WPL。
  2. 优先队列:我们使用了优先队列(具有自定义比较函数的priority_queue)来帮助创建Huffman树。优先队列能够保证在任何时候,都能以对数时间复杂度获取(并删除)最小元素。
  3. 哈希表:我们使用了哈希表(map)来存储字符到频率的映射(在创建Huffman树时)以及字符到Huffman编码的映射(在生成Huffman编码时)。

算法思想

  1. Huffman编码:Huffman编码是一种用于无损数据压缩的贪心算法。该算法使用字符的频率信息来构建一个最优的前缀编码,使得频率高的字符有更短的编码,频率低的字符有更长的编码,从而达到压缩数据的目的。
  2. 深度优先搜索:在生成Huffman编码和计算WPL时,我们使用了深度优先搜索(DFS)。DFS是一种用于遍历或搜索树或图的算法。这种算法会尽可能深地搜索树的分支。在这里,我们使用DFS来遍历Huffman树,并在遍历过程中生成Huffman编码或计算WPL。
  3. 递归:在多个地方,我们使用了递归的方法,如在DFS中遍历二叉树,以及在计算WPL时递归地计算子树的WPL。递归是一种将问题分解为更小子问题的方法,直到问题可以直接解决为止。

(2)设计表示:

主程序各子函数:

  1. Node(char data, int freq):这是一个构造函数,用于创建一个新的节点。它接受一个字符和一个频率作为参数,并初始化一个新的节点。
  2. generateHuffmanCode(Node* root, string str, map<char, string>& huffmanCode):这是一个私有方法,用于生成Huffman编码。它递归地遍历Huffman树,并在遍历过程中生成Huffman编码。
  3. calculateWPL(Node* root, int depth):这是一个私有方法,用于计算WPL(加权路径长度)。它递归地遍历Huffman树,并在遍历过程中计算WPL。
  4. createHuffmanTree(string text):这是一个公有方法,用于创建Huffman树。它首先统计每个字符出现的频率,然后使用优先队列创建Huffman树。
  5. generateHuffmanCode():这是一个公有方法,用于生成Huffman编码。它调用generateHuffmanCode(Node* root, string str, map<char, string>& huffmanCode)方法来生成Huffman编码。
  6. calculateWPL():这是一个公有方法,用于计算WPL。它调用calculateWPL(Node* root, int depth)方法来计算WPL。

图1 函数调用流程图


三、编码说明

完整代码展示:

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <queue>
using namespace std;// 定义节点类
class Node {
public:char data;  // 字符int freq;  // 频率Node* left;  // 左子节点Node* right;  // 右子节点Node(char data, int freq) {this->data = data;this->freq = freq;left = right = nullptr;}
};// 定义比较类
class Compare {
public:bool operator()(Node* l, Node* r) {return l->freq > r->freq;}
};// 定义Huffman树类
class HuffmanTree {
private:Node* root;  // Huffman树的根节点// 生成Huffman编码的方法void generateHuffmanCode(Node* root, string str, map<char, string>& huffmanCode) {if (root == nullptr)return;if (!root->left && !root->right) {huffmanCode[root->data] = str;}generateHuffmanCode(root->left, str + "0", huffmanCode);generateHuffmanCode(root->right, str + "1", huffmanCode);}// 计算WPL的方法int calculateWPL(Node* root, int depth) {if (root == nullptr)return 0;if (!root->left && !root->right)return depth * root->freq;return calculateWPL(root->left, depth + 1) + calculateWPL(root->right, depth + 1);}public:// 创建Huffman树的方法map<char, int> createHuffmanTree(string text) {map<char, int> freqMap;for (char ch : text) {freqMap[ch]++;}priority_queue<Node*, vector<Node*>, Compare> pq;for (auto pair : freqMap) {pq.push(new Node(pair.first, pair.second));}while (pq.size() != 1) {Node* left = pq.top(); pq.pop();Node* right = pq.top(); pq.pop();int sum = left->freq + right->freq;Node* newNode = new Node('\0', sum);newNode->left = left;newNode->right = right;pq.push(newNode);}root = pq.top();return freqMap;}// 生成Huffman编码的方法map<char, string> generateHuffmanCode() {map<char, string> huffmanCode;generateHuffmanCode(root, "", huffmanCode);return huffmanCode;}// 计算WPL的方法int calculateWPL() {return calculateWPL(root, 0);}
};int main() {HuffmanTree huffmanTree;string text;string filename;cout << "请输入文件名:";cin >> filename;  // 从键盘读取文件名// 从文件读取字符串ifstream file(filename, ios::binary);if (file.is_open()) {text = string((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());file.close();}else {cout << "无法打开文件" << endl;return 1;}// 创建Huffman树并统计每个字符出现的频率map<char, int> freqMap = huffmanTree.createHuffmanTree(text);cout << "字符频率:" << endl;for (auto pair : freqMap) {cout << pair.first << ": " << pair.second << endl;}// 输出每个字符的Huffman编码map<char, string> huffmanCode = huffmanTree.generateHuffmanCode();cout << "Huffman编码:" << endl;for (auto pair : huffmanCode) {cout << pair.first << ": " << pair.second << endl;}// 计算并输出WPLint wpl = huffmanTree.calculateWPL();cout << "WPL: " << wpl << endl;return 0;
}

在开发这个程序的过程中,我经历了一些挑战,这导致我需要进行多次迭代才能得到一个满足需求的版本。以下是我在这个过程中遇到的主要困难和我是如何解决它们的:

  1. 文件读取:在最初的版本中,我发现程序无法从文件中读取字符串,只能接受键盘输入。这限制了程序的灵活性,因为用户可能希望直接从文件中读取数据,而不是手动输入。
  2. 版本迭代:为了解决上述问题,我进行了第二次迭代,修改了代码以支持从文件中读取字符串。这个改进使得程序能够处理更复杂的情况,提高了其实用性。

通过这两个版本的迭代,我最终得到了一个可以从文件中读取字符串的程序。虽然这个过程中遇到了一些困难,但每一个挑战都使我有机会学习和成长,也使得最终的程序更加强大和灵活。


四、程序分析

图2 运行结果图

时空性能分析如下:

1.时间复杂度创建Huffman树的时间复杂度是O(n log n),其中n是字符的种类数。这是因为我们需要n次插入操作和n-1次删除操作,每次操作的时间复杂度都是O(log n)。生成Huffman编码和计算WPL的时间复杂度都是O(n),因为我们需要遍历整个Huffman树。

2.空间复杂度:在整个过程中,我们需要存储整个Huffman树,所以空间复杂度是O(n)。此外,我们还需要额外的空间来存储字符到频率的映射和字符到Huffman编码的映射,但这些空间的大小都是常数,所以不影响总的空间复杂度。


五、小结

在解决这个问题的过程中,我深刻地体会到了数据结构,尤其是Huffman树,在解决实际问题中的重要性。Huffman树这种特殊的二叉树结构,以其独特的优雅和高效性,给我留下了深刻的印象。通过这个挑战,我对Huffman树的构建、遍历、修改和查找等操作有了更深入的理解。

我要向我的数据结构老师,表达我最深的感谢。是她的悉心教导让我对数据结构有了深入的理解。她充满热情的教学和专业的知识激发了我对数据结构的热爱,并在我解决这个问题的过程中给予了我宝贵的指导。

此外,我要感谢数据结构这门课程。通过学习这门课程,我不仅掌握了如何更有效地组织和处理数据,还学会了如何分析和解决问题。这个过程对我产生了深远的影响,使我对计算机科学有了更深入的理解。

在解决问题的过程中,我也意识到了一些可以改进的地方。例如,我在创建Huffman树的过程中,未能充分考虑到用户可能输入无效的前序序列。未来,我将对我的代码进行改进,增加对用户输入的验证,以确保创建的Huffman树的有效性。

通过解决这个问题,我对Huffman树有了更深的理解,我的编程技能也得到了提升。在未来,我将继续深入学习和探索数据结构,以更好地应对各种实际问题。再次感谢我的数据结构老师和这门课程,让我有机会领略到数据结构的魅力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/689949.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于服务端接口知识的汇总

大家好&#xff0c;今天给大家分享一下之前整理的关于接口知识的汇总&#xff0c;对于测试人员来说&#xff0c;深入了解接口知识能带来诸多显著的好处。 一、为什么要了解接口知识&#xff1f; 接口是系统不同模块之间交互的关键通道。只有充分掌握接口知识&#xff0c;才能…

【联通支付注册/登录安全分析报告】

联通支付注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…

Coursera吴恩达深度学习专项课程01: Neural Networks and Deep Learning 学习笔记 Week 01

Week 01 of Neural Networks and Deep Learning Course Certificate 本文是学习 https://www.coursera.org/learn/neural-networks-deep-learning 这门课的笔记 Course Intro 文章目录 Week 01 of Neural Networks and Deep Learning[0] Welcome to the Deep Learning Spec…

用python写个控制MicroSIP自动拨号和定时呼叫功能(可用在小型酒店叫醒服务)MicroSIP定时拨号

首先直接上结果吧&#xff0c;MicroSIP 助手&#xff0c;控制MicroSIP自动拨号&#xff0c;定时呼叫的非常实用小工具&#xff01; 在使用MicroSIP 助手之前&#xff0c;我们需要了解MicroSIP是什么&#xff0c;MicroSIP是一个SIP拨号软件&#xff0c;支持注册任意SIP平台实现拨…

详细解读jmeter如何做接口测试

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Jmeter介绍&测试准备&#xff1a; Jmeter介绍&#xff1a;…

Offline: Overcoming Model Bias for Robust Offline Deep Reinforcement Learning

EAAI 2023 paper Intro model-free的离线强化学习由于价值函数估计问题存在训练的稳定性以及鲁棒性较低。本文提出基于模型的方法&#xff0c;同构构建稳定的动力学模型帮助策略的稳定训练。 method 本文基于模型的方法&#xff0c;所构造的转移模型输入状态动作&#xff0…

使用OpenCV实现图像平移

使用OpenCV实现图像平移 程序流程效果代码 程序流程 读取图像并获取其高度、宽度和通道数。定义平移量tx和ty&#xff0c;并创建平移矩阵M。使用cv2.warpAffine函数对图像进行仿射变换&#xff08;平移&#xff09;&#xff0c;得到平移后的图像。显示平移后的图像。等待用户按…

【qt】设计器实现界面

设计器实现界面 一.总体思路二.具体操作1.创建项目2.粗略拖放3.水平布局4.垂直布局5.修改名字6.转到槽7.实现槽函数 一.总体思路 二.具体操作 1.创建项目 这次咱们一定要勾选Generate form哦。 因为我们要使用设计器进行拖放。 2.粗略拖放 这里用到了复选框&#xff1a;C…

异构图神经网络代码详解与实战

相关代码地址见文末 1.数据读取 数据采用的是电影推荐的数据集,movies.csv文件存储为电影及其题材。 ratings.csv下存储为用户对电影的评分。 数据集的读取流程为: 首先,读取movies.csv并将题材根据词的出现,转换为one-hot编码的形式读取ratings.csv,将movie_id和…

【C语言—猜数字小游戏】

一、游戏规则 电脑自动生成一个1~100范围内的随机数&#xff0c;由玩家猜测本轮生成的随机数是什么&#xff0c;系统根据玩家猜测数据的⼤⼩给出猜⼤了或猜⼩了的反馈&#xff0c;直到玩家猜对&#xff0c;游戏结束。 如何生成随机数&#xff1a;【C语言】/*如何生成随机值*/-C…

MySQL系列之索引

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

使用AudioCraft(MusicGen)生成音乐

AudioCraft 是一个 PyTorch 库,用于音频生成的深度学习研究。AudioCraft 包含两个先进的AI生成模型:AudioGen和MusicGen,它们共同致力于生成高质量的音频内容。 MusicGen是一个简单且可控的音乐生成模型。它利用Meta提供的20K小时授权音乐进行训练,能够根据文本描述或已有…