当前位置: 首页 > news >正文

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

相关文章:

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

一、问题描述 问题概括&#xff1a; 给定一个字符串或文件&#xff0c;基于Huffman编码方法&#xff0c;实现以下功能&#xff1a; 1.统计每个字符的频率。 2.输出每个字符的Huffman编码。 3.计算并输出WPL&#xff08;加权路径长度&#xff09;。 这个问题要求对Huffman编码算…...

处理VS2022中(C/C++)scanf报错问题(3种)

#pragma warning(disable:4996)//第一种&#xff1a;处理scanf在VS2022中报错 #define _CRT_SECURE_NO_WARNINGS//第二种:处理scanf在VS2022中报错 #include<bits/stdc.h> using namespace std; int main() { int a, b; scanf(“%d %d”, &a, &b);//第三种&…...

C#面:Session 喜欢丢值且占内存,Cookis不安全,请问 C# 可以用什么办法代替这两种原始的方法

可以使用 用 ViewState&#xff0c;stateserver。 在 C# 中&#xff0c;ViewState、StateServer 和 Session 都是用于在 Web 应用程序中存储和管理状态信息的机制。它们可以用来在不同的页面之间传递数据或者在同一页面的不同请求之间保持数据的持久性。 ViewState&#xff1…...

Python并发编程 05 锁、同步条件、信号量、线程队列、生产者消费者模型

文章目录 一、基础概念二、同步锁三、线程死锁和递归锁四、同步条件&#xff08;event&#xff09;五、信号量六、线程队列&#xff08;queue&#xff09;1、常用方法2、queue模块的三种模式&#xff08;1&#xff09;FIFO队列&#xff08;2&#xff09;LIFO队列&#xff08;3&…...

UIKit之UIButton

功能需求&#xff1a; 点击按钮切换按钮的文字和背景图片&#xff0c;同时点击上下左右可以移动图片位置&#xff0c;点击加或减可以放大或缩小图片。 分析&#xff1a; 实现一个UIView的子类即可&#xff0c;该子类包含多个按钮。 实现步骤&#xff1a; 使用OC语言&#xf…...

阿里云VOD视频点播流程(2)

二、视频点播 1、入门代码 基于OSS原生SDK上传 &#xff0c;参考文档&#xff1a;https://help.aliyun.com/zh/vod/user-guide/upload-media-files-by-using-oss-sdks?spma2c4g.11186623.0.0.1f02273fj4lxNJ 视频点播面向开发者提供了丰富的上传方式&#xff0c;其中上传SDK&…...

在Ubuntu上搭建幻兽帕鲁服务器

简介 幻兽帕鲁是一款多人在线角色扮演游戏&#xff0c;玩家可以捕捉和训练各种各样的幻兽&#xff0c;并与其他玩家进行战斗和交易。如果您想拥有自己的幻兽帕鲁服务器&#xff0c;可以按照以下步骤在 Ubuntu 上进行搭建。 准备工作 在开始之前&#xff0c;您需要准备以下几…...

Java中常用类String的不可变性详解

目录 一、String类的概述 二、String不可变性的原理 三、String不可变性的优点 四、String不可变性的缺点及解决方案 五、总结 一、String类的概述 在Java中&#xff0c;String类是一个代表字符串的类。它是Java核心API的一部分&#xff0c;用于处理文本数据。String对象…...

uniapp 自定义App UrlSchemes

需求&#xff1a;外部浏览器H5页面&#xff0c;跳转到uniapp开发的原生app内部。 1、uniapp内部的配置&#xff1a; &#xff08;1&#xff09;打开manifest->App常用其他设置&#xff0c;如下&#xff0c;按照提示输入您要设置的urlSchemes&#xff1a; &#xff08;2&am…...

MSP430环境搭建

1.下载ccs编译器 注意&#xff1a;安装路径和工作路径不能出现中文&#xff01; 没有说明的步骤就点next即可&#xff01; 1.1下载适合自己电脑的压缩包。 下载好压缩包后解压&#xff0c;点击有图标进行安装。 1.2创建一个文件夹用于安装编译器位置 选择安装地址&#xff0…...

【Qt C++实现蓝牙互联】

在 Qt C++ 中实现蓝牙互联可以通过 Qt 的蓝牙模块来实现。下面是一个简单的示例,演示如何在 Qt C++ 中使用蓝牙模块进行蓝牙互联,实现搜索设备、连接设备等功能。 // main.cpp #include <QCoreApplication> #include <QBluetoothDeviceDiscoveryAgent> #include…...

AI绘画已如此厉害,为何我们仍需学习绘画?

在这个AI技术日新月异的时代&#xff0c;AI绘画能力的大幅提升已经不是什么新鲜事。它们以惊人的速度和惊人的精细度完成作品&#xff0c;让不少人感叹&#xff1a;“这是不是意味着&#xff0c;未来绘画将完全由AI接管&#xff0c;人类的创作将变得无足轻重&#xff1f;”在这…...

Android 实现背景图片不被拉伸的效果 9-patch图片 .9图

今天碰到个需求&#xff0c;要求不同手机分辨率背景照片不能被拉伸&#xff0c;除了调用系统方法计算当前屏幕大小这个方法外还有一个就是9-patch图片&#xff0c;可以实现除了icon剩下的部位被缩放。 方法&#xff1a;资源文件右击找到9-patch&#xff0c;转为XXX.9.png照片 …...

Java EE/Jakarta EE范畴一览

Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;现在已经改名为Jakarta EE&#xff0c;是一套用于开发企业级应用的标准Java平台。它扩展了Java SE&#xff08;Standard Edition&#xff09;&#xff0c;添加了支持大规模、多层次、可靠、安全、可伸缩和可管…...

洛谷 P3391:文艺平衡树 ← Splay树模板题

【题目来源】https://www.luogu.com.cn/problem/P3391【题目描述】 您需要写一种数据结构&#xff08;可参考题目标题&#xff09;&#xff0c;来维护一个有序数列。 其中需要提供以下操作&#xff1a;翻转一个区间&#xff0c;例如原有序序列是 5 4 3 2 1&#xff0c;翻转区间…...

【高校科研前沿】北师大陈晋教授团队在遥感顶刊发表最新成果:ClearSCD模型:在高空间分辨率遥感影像中综合利用语义和变化关系进行语义变化检测

01文章简介 论文名称&#xff1a;The ClearSCD model: Comprehensively leveraging semantics and change relationships for semantic change detection in high spatial resolution remote sensing imagery&#xff08;ClearSCD模型&#xff1a;在高空间分辨率遥感影像中综合…...

关于YOLO8学习(五)安卓部署ncnn模型--视频检测

教学视频地址 B站 前文 关于YOLO8学习(一)环境搭建,官方检测模型部署到手机 关于YOLO8学习(二)数据集收集,处理 关于YOLO8学习(三)训练自定义的数据集 关于YOLO8学习(四)模型转换为ncnn 简介 本文将会讲解: (1)使用前文生成的ncnn模型,部署到安卓端,并且实…...

从哪些方面可以看出光伏的未来发展好?

光伏发电是一种基于半导体材料的光伏效应将太阳光转化为直流电能的发电技术。近年来&#xff0c;随着全球对可再生能源和环境保护的关注度不断提升&#xff0c;光伏发电行业发展迅速&#xff0c;成为未来能源领域的重要发展方向。 首先&#xff0c;从能源角度来看&#xff0c;光…...

VBA_MF系列技术资料1-605

MF系列VBA技术资料1-605 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…...

算法题① —— 数组专栏

1. 滑动窗口 1.1 长度最小的子数组 力扣&#xff1a;https://leetcode.cn/problems/minimum-size-subarray-sum/description/ int minSubArrayLen(int s, vector<int>& nums) {int result INT32_MAX; int sum 0; // 子序列的数值之和int subLength 0; // 子序列…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...