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

哈夫曼树(构建、编码、译码)(详细分析+C++代码实现)

 D 哈夫曼树

题目要求

编写一个哈夫曼编码译码程序。针对一段文本,根据文本中字符出现频率构造哈夫曼树,给出每个字符的哈夫曼编码,并进行译码,计算编码前后文本大小。
为确保构建的哈夫曼树唯一,本题做如下限定:

  1. 选择根结点权值最小的两棵二叉树时,选取权值较小者作为左子树。
  2. 若多棵二叉树根结点权值相等,则先生成的作为左子树,后生成的作为右子树,具体来说:i) 对于单结点二叉树,优先选择根结点对应字母在文本中最先出现者,如文本为cba,三个字母均出现1次,但c在文本中最先出现,b第二出现,故则选择c作为左子树,b作为右子树。ii) 对于非单结点二叉树,先生成的二叉树作为左子树,后生成的二叉树作为右子树。iii. 若单结点和非单结点二叉树根结点权值相等,优先选择单结点二叉树。
  3. 生成哈夫曼编码时,哈夫曼树左分支标记为0,右分支标记为1。

输入格式:

输入为3行。第1行为一个字符串,包含不超过5000个字符,至少包含两个不同的字符,每个字符为a-z的小写字母。第2、3行为两个由0、1组成的字符串,表示待译码的哈夫曼编码。

输出格式:

输出第一行为用空格间隔的2个整数,分别为压缩前后文本大小,以字节为单位,一个字符占1字节,8个二进制位占1字节,若压缩后文本不足8位,则按1字节算。输出从第二行开始,每行为1个字符的哈夫曼编码,按各字符在文本中出现次数递增顺序输出,若多个字符出现次数相同,则按其在文本出现先后排列。每行格式为“字母:编码”。最后两行为两行字符串,表示译码结果,若译码失败,则输出INVALID。

输入样例:

cbaxyyzz
0100
011

输出样例:

8 3
c:100
b:101
a:110
x:111
y:00
z:01
zy
INVALID

题目分析

要点1:原文本字符数据整理

根据输入的字符串,整理字符的种类数,以及各字符的个数,并将其按照出现次数从小到大进行排列,若次数相同,则先出现的仍在前。

//数据预处理
//计算输入的文本出现的所有不同的字符和对应数量
const int N = 5010;
int h[N], idx,w[N];  //w数组存储字符在文本中出现的个数,idx最终保存不同的字符种类数,h数组存储对应字符的下标
char da[N];  //da数组存储字符
int PreLengh;  //初始文本的长度
string line;  //初始文本
void input() {cin >> line;PreLengh = line.size();for (char ch : line) {  //遍历文本中的每一个字符if (w[h[ch]] == 0) {  //字符的权重为0,该字符第一次出现da[++idx] = ch;h[ch] = idx;w[idx] = 1;}else {w[h[ch]]++;  //否则,不是第一次出现,权重+1}}//数据录入结束,进行排序//冒泡排序,从小到大排列,权重相同的原来在前仍在前for (int i = 1; i <= idx; i++) {for (int j = 1; j <= idx - 1; j++) {if (w[j] > w[j + 1]) {swap(w[j], w[j + 1]);swap(da[j], da[j + 1]);  //权重和数据都要交换}}}
}

要点2:构建huffman树

1.在森林中取权值最小的两个根结点s和nl,合并成一棵二叉树,并生成一个新结点T作为这两个结点的父亲,T的权值是它的两个子结点的权值之和。

2.对新森林重复上一步操作,直至森林中只有唯一的根结点时,终止操作。 

//创建哈夫曼树
HuffmanTree* createHuffmanTree(char data[],int weight[],int n) {HuffmanTree* tree=new HuffmanTree;tree->m = n;  //结点总数tree->H = new HuffmanNode * [tree->m + 1];HuffmanNode* p1, * p2, * p, * t;//初始化结点for (int i = 1; i <= tree->m; i++) {tree->H[i] = new HuffmanNode;tree->H[i]->INFO = data[i];tree->H[i]->Weight = weight[i];tree->H[i]->LLINK = NULL;tree->H[i]->RLINK = NULL;}//组合结点int i, j;for (int i = 1; i < tree->m; i++) {  //遍历所有结点t = new HuffmanNode;p1 = tree->H[i];  //选取最小的两个结点作为左右子树p2 = tree->H[i + 1];t->LLINK = p1;t->RLINK = p2;t->Weight = p1->Weight + p2->Weight;p = t;j = i + 2;//比较排列,仍要保证从小到大排列while (j <= tree->m && (p->Weight) >= tree->H[j]->Weight) {tree->H[j - 1] = tree->H[j];j++;}//将新生成的树放入森林中tree->H[j - 1] = p;}return tree;
}

要点3:Huffman编码

要输出所有字符的编码,遍历思想,走左子树则+0,走右子树则+1,直至走到叶结点,为字符,存储为对应字符的Huffman编码。

//Huffman编码
//char标志字符,与其对应的Huffman编码
typedef unordered_map<char, string> UMCS;
UMCS HuffmanCode;
void CreateHuffmanCode(HuffmanNode* root, string code) {if (root == NULL) return;if (!root->LLINK && !root->RLINK) {  //如果是叶结点,遍历到字符HuffmanCode[root->INFO] = code; }CreateHuffmanCode(root->LLINK, code + "0");  //左子树+0CreateHuffmanCode(root->RLINK, code + "1");  //右子树+1
}

 要点4:对二进制进行译码

读入一整串的二进制数,遇到0就走左子树,遇到1就走右子树,直至走到叶结点,为字符,一个字符到此译码成功,将该字符串到总答案中。若此时还有编码剩余,则重新从树根开始,继续译码,直至读入全部二进制编码。

若全部二进制读入完毕,但此时指针不位于叶结点,证明译码失败,没有正确结束。输出"INVALID"。

//对二进制进行译码
void TransHuffmanCode(HuffmanNode* root) {HuffmanNode* t = root;for (int num = 2; num > 0; num--) {string op,ans="";cin >> op;  //读入整串的二进制编码for (int i = 0; i < op.size(); i++) {char k = op[i];if (k == '0') t = t->LLINK;  //如果是0,就走左指针if (k == '1') t = t->RLINK;  //如果是1,就走右指针if (!t->LLINK && !t->RLINK) {  //走到叶结点,译码成功,串入答案ansans = ans + t->INFO;if (i != op.size() - 1) t = root;  //若还有编码未译完,重新返回树根,继续译码}}if (!(!t->LLINK && !t->RLINK)) cout<<"INVALID";  //如果译码到最后,没有走到叶结点,证明译码失败else cout << ans;cout << endl;t = root;}
}

完整代码

#include <iostream>
#include <cstring>
#include <unordered_map>
using namespace std;//Huffman结点
typedef struct HuffmanNode {char INFO;  //信息域int Weight;  //权值HuffmanNode* LLINK;  //左链接HuffmanNode* RLINK;  //右链接
}HuffmanNode;//Huffman树的构建
typedef struct HuffmanTree {HuffmanNode** H;  //存储哈夫曼树结点的数组Hint m;  //哈夫曼树结点总数
}HuffmanTree;//数据预处理
//计算输入的文本出现的所有不同的字符和对应数量
const int N = 5010;
int h[N], idx,w[N];  //w数组存储字符在文本中出现的个数,idx最终保存不同的字符种类数,h数组存储对应字符的下标
char da[N];  //da数组存储字符
int PreLengh;  //初始文本的长度
string line;  //初始文本
void input() {cin >> line;PreLengh = line.size();for (char ch : line) {  //遍历文本中的每一个字符if (w[h[ch]] == 0) {  //字符的权重为0,该字符第一次出现da[++idx] = ch;h[ch] = idx;w[idx] = 1;}else {w[h[ch]]++;  //否则,不是第一次出现,权重+1}}//数据录入结束,进行排序//冒泡排序,从小到大排列,权重相同的原来在前仍在前for (int i = 1; i <= idx; i++) {for (int j = 1; j <= idx - 1; j++) {if (w[j] > w[j + 1]) {swap(w[j], w[j + 1]);swap(da[j], da[j + 1]);  //权重和数据都要交换}}}
}//创建哈夫曼树
HuffmanTree* createHuffmanTree(char data[],int weight[],int n) {HuffmanTree* tree=new HuffmanTree;tree->m = n;  //结点总数tree->H = new HuffmanNode * [tree->m + 1];HuffmanNode* p1, * p2, * p, * t;//初始化结点for (int i = 1; i <= tree->m; i++) {tree->H[i] = new HuffmanNode;tree->H[i]->INFO = data[i];tree->H[i]->Weight = weight[i];tree->H[i]->LLINK = NULL;tree->H[i]->RLINK = NULL;}//组合结点int i, j;for (int i = 1; i < tree->m; i++) {  //遍历所有结点t = new HuffmanNode;p1 = tree->H[i];  //选取最小的两个结点作为左右子树p2 = tree->H[i + 1];t->LLINK = p1;t->RLINK = p2;t->Weight = p1->Weight + p2->Weight;p = t;j = i + 2;//比较排列,仍要保证从小到大排列while (j <= tree->m && (p->Weight) >= tree->H[j]->Weight) {tree->H[j - 1] = tree->H[j];j++;}//将新生成的树放入森林中tree->H[j - 1] = p;}return tree;
}//Huffman编码
//char标志字符,与其对应的Huffman编码
typedef unordered_map<char, string> UMCS;
UMCS HuffmanCode;
void CreateHuffmanCode(HuffmanNode* root, string code) {if (root == NULL) return;if (!root->LLINK && !root->RLINK) {  //如果是叶结点,遍历到字符HuffmanCode[root->INFO] = code; }CreateHuffmanCode(root->LLINK, code + "0");  //左子树+0CreateHuffmanCode(root->RLINK, code + "1");  //右子树+1
}//计算压缩后文本的大小
int PostLength = 0;
void PostNum(UMCS HuffmanCode) {for (char k : line) {  //从头到位按照原文本的逐一计算PostLength += HuffmanCode[k].size();}PostLength = (PostLength + 7) / 8;  //以字节为单位计算,不足8位,按一字节算
}//打印输出huffman编码
void printHuffmanCode() {for (int i = 1; i <= idx; i++) {  //da数组内存储的即为数据字符cout << da[i] << ":" << HuffmanCode[da[i]] << endl;}
}//对二进制进行译码
void TransHuffmanCode(HuffmanNode* root) {HuffmanNode* t = root;for (int num = 2; num > 0; num--) {string op,ans="";cin >> op;  //读入整串的二进制编码for (int i = 0; i < op.size(); i++) {char k = op[i];if (k == '0') t = t->LLINK;  //如果是0,就走左指针if (k == '1') t = t->RLINK;  //如果是1,就走右指针if (!t->LLINK && !t->RLINK) {  //走到叶结点,译码成功,串入答案ansans = ans + t->INFO;if (i != op.size() - 1) t = root;  //若还有编码未译完,重新返回树根,继续译码}}if (!(!t->LLINK && !t->RLINK)) cout<<"INVALID";  //如果译码到最后,没有走到叶结点,证明译码失败else cout << ans;cout << endl;t = root;}
}
int main() {//数据预处理input();//创建Huffman树HuffmanTree* tree = createHuffmanTree(da, w, idx);//构造Huffman编码CreateHuffmanCode(tree->H[idx], "");//计算编码后文本大小PostNum(HuffmanCode);//输出压缩前后文本大小cout << PreLengh << ' ' << PostLength << endl;//输出各字符的Huffman编码printHuffmanCode();//对输入的Huffman二进制编码进行译码TransHuffmanCode(tree->H[idx]);return 0;
}

 提交结果

相关文章:

哈夫曼树(构建、编码、译码)(详细分析+C++代码实现)

D 哈夫曼树 题目要求 编写一个哈夫曼编码译码程序。针对一段文本&#xff0c;根据文本中字符出现频率构造哈夫曼树&#xff0c;给出每个字符的哈夫曼编码&#xff0c;并进行译码&#xff0c;计算编码前后文本大小。 为确保构建的哈夫曼树唯一&#xff0c;本题做如下限定&…...

C++ 二叉搜索树

目录 概念 性能分析 二叉搜索树的插入 二叉树的查找 二叉树的前序遍历 二叉搜索树的删除&#xff08;重点&#xff09; 完整代码 key与value的使用 概念 对于一个二叉搜索树 若它的左子树不为空&#xff0c;则左子树上所有的节点的值都小于等于根节点的值若它的右子树不为空…...

docker构建Java项目镜像常用的Java版本,国内私有仓库公网快速下载,解决从docker.io无法下载的问题

2015工作至今&#xff0c;10年资深全栈工程师&#xff0c;CTO&#xff0c;擅长带团队、攻克各种技术难题、研发各类软件产品&#xff0c;我的代码态度&#xff1a;代码虐我千百遍&#xff0c;我待代码如初恋&#xff0c;我的工作态度&#xff1a;极致&#xff0c;责任&#xff…...

低代码系统-氚云、简道云表单控件对比

组件对比 氚云 简道云 是否都有 1 单行文本 单行文本 ☑️ 2 多行文本 多行文本 ☑️ 3 日期 日期时间 ☑️ 4 数字 数字 ☑️ 5 单选框 单选按钮组 ☑️ 6 复选框 复选框组 ☑️ 7 下拉框 下拉框 ☑️ 8 附件 附件 ☑️ 9 图片 图片 ☑️ 10 地址 地…...

为什么IDEA提示不推荐@Autowired❓️如果使用@Resource呢❓️

前言 在使用 Spring 框架时&#xff0c;依赖注入&#xff08;DI&#xff09;是一个非常重要的概念。通过注解&#xff0c;我们可以方便地将类的实例注入到其他类中&#xff0c;提升开发效率。Autowired又是被大家最为熟知的方式&#xff0c;但很多开发者在使用 IntelliJ IDEA …...

Unity在WebGL中拍照和录视频

原工程地址https://github.com/eangulee/UnityWebGLRecoder Unity版本2018.3.6f1&#xff0c;有点年久失修了 https://github.com/xue-fei/Unity.WebGLRecorder 修改jslib适配了Unity2021 效果图 录制的视频 Unity在WebGL中拍照和录视频...

爬虫基础之爬取某站视频

目标网址:为了1/4螺口买小米SU7&#xff0c;开了一个月&#xff0c;它值吗&#xff1f;_哔哩哔哩_bilibili 本案例所使用到的模块 requests (发送HTTP请求)subprocess(执行系统命令)re (正则表达式操作)json (处理JSON数据) 需求分析: 视频的名称 F12 打开开发者工具 or 右击…...

mongoDB常见指令

即使我们自己开发用不到mongoDB&#xff0c;但是接手别人项目的时候&#xff0c;别人如果用了&#xff0c;我们也要会简单调试一下 虽然mongoDB用的不是sql语句&#xff0c;但语句的逻辑都是相似的&#xff0c;比如查看数据库、数据表&#xff0c;增删改查这些 我们下面以doc…...

人工智能之深度学习_[5]-神经网络优化学习率衰减优化正则化方法

文章目录 神经网络入门二3 神经网络优化方法3.1 梯度下降算法回顾3.2 反向传播&#xff08;BP算法&#xff09;3.2.1 反向传播概念3.2.2 反向传播详解 3.3 梯度下降优化方法3.3.1 指数加权平均3.3.2 动量算法Momentum3.3.3 AdaGrad3.3.4 RMSProp3.3.5 Adam3.3.6 小结 4 学习率衰…...

Oracle之Merge into函数使用

Merge into函数为Oracle 9i添加的语法&#xff0c;用来合并update和insert语句。所以也经常用于update语句的查询优化&#xff1a; 一、语法格式&#xff1a; merge into A using B on (A.a B.a) --注意on后面带括号&#xff0c;且不能更新join的字段 when matched then upd…...

深度解析:哪种心磁图技术是心脏检查的精准之选?

在全球心血管疾病的阴影日益笼罩的今天&#xff0c;医学界正积极寻求一种无损、无创、无辐射的心脏健康监测方式。心磁图仪&#xff08;MCG&#xff09;&#xff0c;这一前沿技术&#xff0c;凭借其独特的优势&#xff0c;悄然成为心脏电磁功能监测的新星。它不仅为心肌缺血、心…...

SpringBoot--基本使用(配置、整合SpringMVC、Druid、Mybatis、基础特性)

这里写目录标题 一.介绍1.为什么依赖不需要写版本&#xff1f;2.启动器(Starter)是何方神圣&#xff1f;3.SpringBootApplication注解的功效&#xff1f;4.启动源码5.如何学好SpringBoot 二.SpringBoot3配置文件2.1属性配置文件使用2.2 YAML配置文件使用2.3 YAML配置文件使用2.…...

单片机-STM32 IIC通信(OLED屏幕)(十一)

一、屏幕的分类 1、LED屏幕&#xff1a; 由无数个发光的LED灯珠按照一定的顺序排列而成&#xff0c;当需要显示内容的时候&#xff0c;点亮相关的LED灯即可&#xff0c;市场占有率很高&#xff0c;主要是用于户外&#xff0c;广告屏幕&#xff0c;成本低。 LED屏是一种用发光…...

观察者模式 - 观察者模式的应用场景

引言 观察者模式&#xff08;Observer Pattern&#xff09;是设计模式中行为型模式的一种&#xff0c;它定义了对象之间的一对多依赖关系&#xff0c;使得当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都会自动收到通知并更新。观察者模式广泛应用于事件处理系统…...

【C++】详细讲解继承(下)

本篇来继续说说继承。上篇可移步至【C】详细讲解继承&#xff08;上&#xff09; 1.继承与友元 友元关系不能继承 &#xff0c;也就是说基类友元不能访问派⽣类私有和保护成员。 class Student;//前置声明class Same //基类 { public:friend void Fun(const Same& p, con…...

消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)

Apache Pulusar是一个分布式、多租户、高性能的发布/订阅&#xff08;Pub/Sub&#xff09;消息系统&#xff0c;最初由Yahoo开发并开源。它结合了Kafka和传统消息队列的优点&#xff0c;提供高吞吐量、低延迟、强一致性和可扩展的消息传递能力&#xff0c;适用于大规模分布式系…...

扬帆数据结构算法之舟,启航C++探索征途——LeetCode深度磨砺:顺序表技术精进实践

人无完人&#xff0c;持之以恒&#xff0c;方能见真我&#xff01;&#xff01;&#xff01; 共同进步&#xff01;&#xff01; 文章目录 顺序表练习1.移除数组中指定的元素方法1&#xff08;顺序表&#xff09;方法2&#xff08;双指针&#xff09; 2.删除有序数组中的重复项…...

基于本地事务表+MQ实现分布式事务

基于本地事务表MQ实现分布式事务 引言1、原理2、本地消息表优缺点3、代码实现3.1、代码执行流程3.2、项目结构3.3、项目源码 引言 本地消息表的方案最初由ebay的工程师提出&#xff0c;核心思想是将分布式事务拆分成本地事务进行处理。本地消息表实现最终一致性。本文主要学习…...

数据结构:二叉树—面试题(一)

目录 1、相同的树 2、另一棵树的子树 3、翻转二叉树 4、平衡二叉树 5、对称二叉树 6、二叉树遍历 7、二叉树的分层遍历 1、相同的树 习题链接https://leetcode.cn/problems/same-tree/description/https://leetcode.cn/problems/same-tree/description/ 描述&#xff1a…...

【Wordpress网站制作】切换语言的问题

前言 自学笔记&#xff0c;解决问题为主&#xff0c;欢迎补充。 本文重点&#xff1a;如何将页面语言从默认的【英语】修改成【中文】。 问题描述 安装完wordpress&#xff0c;在【Setting】→【General】的语言中&#xff0c;选项只有英语。无法切换成中文 方法1: 在 wp-c…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...