浙大数据结构:05-树9 Huffman Codes

news2024/11/23 15:26:40
这道题难度挺大,写起来较为费劲,这里我依然使用了STL库,使得代码量大幅减少不过百行,便于大家理解。
机翻:

1、条件准备 

数组存储字符对应频率,n,student存储输入多少字符,有多少学生测试。
wpl为最小带权路径长度,后边用到了multiset,分别来算最小带权路径长度和判断学生数据是否成立。后面看到代码再细说。
#include <iostream>
#include<string>
#include <set>
using namespace std;

int num[128];//存储字符对应的频率
int n,student;//输入多少字符,有多少学生
int wpl;    //带权路径长度
multiset<pair<int,int>> s;//用来算最小的带权路径长度
multiset<string> se;//用来验证输入的数据是否符合要求
   主函数较为简单,先初始化一下,然后计算最小带权路径,然后输入学生,循环判断每个学生是否符合要求
int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);

   init();
   wpl=minwpl();
  
  cin>>student;
  for(int i=0;i<student;i++)
  {
    judge(wpl);
  }
  return 0;
}

2、init函数

输入字符数,然后存储字符和其频率,用num数组存储。
s集合存入数据对,第一个为出现的频率,第二个为0.具体含义后面再说。
void init()
{
   cin>>n;
   for(int i=1;i<=n;i++)
   {
     char c;int a;
     cin>>c>>a;
     s.insert({a,0});
     num[c]=a;
   }
}

3、minwpl函数

如何计算最小带权路径长度呢?我们用哈夫曼树的构造方式来模拟:先取最小的两个合并成一个新的建立小子树,再从堆里选两个,以此类推直到最后合并成一棵树。但是这样只能生成带权路径最小的树,并没有算出带权路径是多少,我们这里采用数据对第二位来保存以该结点为根结点的最小带权路径,经过递推可以算出最终结果。我们具体来看一下。
先取堆顶放入左结点,删除堆顶再取堆顶放入右结点,删除堆顶。接下来准备插入t。
t的权值放在第一位,很好求,左右结点权值相加即可,
t为根节点的wpl初始也为左右结点权值相加,然后判断这个结点是否是后天计算的(非原始的),即第二位不为0,那么就加一下它的第二位,这样能使得权值较小的被多次相加也就算出了WPL.
这里建议画几颗树推一下,否则不是很好理解(我也是想半天想到的)
int minwpl()
{
  if(s.size()==1)return s.begin()->second;
//递归终点,就剩一个点了,就是根节点
  pair<int,int> left=*s.begin();//取最小
  s.erase(s.begin());//删除堆顶
  pair<int,int> right=*s.begin();//取最小
  s.erase(s.begin());//删除堆顶

  pair<int,int> t={left.first+right.first,left.first+right.first};
//建立新的结点
  if(left.second)t.second+=left.second;
  if(right.second)t.second+=right.second;
  s.insert(t);//插入
  return minwpl();//继续递归
}

4、judge函数

对于每个学生的数据,我们直接用每个字符的编码长度*该字符的权值再累加和求出wpl与我们的wpl进行比较即可。
如何判断前缀码是否有重的呢?我把每个输入的编码的所有前缀码存入集合se中,如果有字符的编码能在其中找到则前缀代码有问题,f=1。
最后输出。
PS:这里有个小bug,应该存一下所有编码最后再判断,但这题依然AC了
void judge(int miwpl)//判断wpl、是否重了,输出
{
  int w=0; //算当前student的wpl
se.clear();//清空一下
int f=0;//判断是否前缀码有重的
  for(int i=0;i<n;i++)
  {
    char c;string s;
    cin>>c>>s;
    w+=s.size()*num[c];//算wpl
  
    if(se.find(s)!=se.end())f=1;//不为其它函数的前缀码
//其实这里有bug,应该所有前缀码输入完再一个个判断,但这题还能AC
     se.insert(s); 
    for(int j=1;j<=s.size();j++)
    {
      se.insert(s.substr(0,j));//插入可能的前缀
    }
  }

  if(w!=miwpl){cout<<"No"<<endl;return ;}
  if(f){cout<<"No"<<endl;return ;}
  cout<<"Yes"<<endl;
}

5、总结

这道题难度非常大,我做了一下午,做完后发现网上很多代码都用C语言实现的,200行左右,我便把C++实现给大家分享一下,主要是给大家提供一些新的思路。
完整代码如下:
#include <iostream>
#include <string>
#include <set>
using namespace std;

int num[128];
int n, student;
int wpl;
multiset<pair<int, int>> s;
multiset<string> se;
void init()
{
  cin >> n;
  for (int i = 1; i <= n; i++)
  {
    char c;
    int a;
    cin >> c >> a;
    s.insert({a, 0});
    num[c] = a;
  }
}

int minwpl()
{
  if (s.size() == 1)
    return s.begin()->second;
  pair<int, int> left = *s.begin();
  s.erase(s.begin());
  pair<int, int> right = *s.begin();
  s.erase(s.begin());
  pair<int, int> t = {left.first + right.first, left.first + right.first};
  if (left.second)
    t.second += left.second;
  if (right.second)
    t.second += right.second;
  s.insert(t);
  return minwpl();
}

void judge(int miwpl) // 判断wpl、是否重了,输出
{
  int w = 0;
  se.clear();
  int f = 0;
  for (int i = 0; i < n; i++)
  {
    char c;
    string s;
    cin >> c >> s;
    w += s.size() * num[c];
    if (se.find(s) != se.end())
      f = 1;
    se.insert(s);
    for (int j = 1; j <= s.size(); j++)
    {
      se.insert(s.substr(0, j));
    }
  }
  if (w != miwpl)
  {
    cout << "No" << endl; return;
  }
  if (f)
  {
    cout << "No" << endl; return;
  }
  cout << "Yes" << endl;
}

int main()
{
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  init();
  wpl = minwpl();
  cin >> student;
  for (int i = 0; i < student; i++)
  {
    judge(wpl);
  }
  return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Transformers基础入门篇2】基础组件之Pipeline

文章目录 一、什么是Pipeline二、查看PipeLine支持的任务类型三、Pipeline的创建和使用3.1 根据任务类型&#xff0c;直接创建Pipeline&#xff0c;默认是英文模型3.2 指定任务类型&#xff0c;再指定模型&#xff0c;创建基于指定模型的Pipeline3.3 预先加载模型&#xff0c;再…

用二维码收集信息时,在后台可以查看、统计哪些数据?

大家都知道&#xff0c;在二维码上关联表单&#xff0c;就可以扫码填写信息了。那么&#xff0c;收集到的数据在哪里查看&#xff1f;具体可以查看到哪些数据呢&#xff1f; 如果是用草料二维码平台搭建的二维码&#xff0c;前往后台&#xff0c;在表单列表中找到对应的表单&a…

智能Ai语音机器人的应用价值有哪些?

随着时间的推移&#xff0c;人工智能的发展越来越成熟&#xff0c;智能时代也离人们越来越近&#xff0c;近几年人工智能越来越火爆&#xff0c;人工智能的应用已经开始渗透到各行各业&#xff0c;与生活交融&#xff0c;成为人们无法拒绝&#xff0c;无法失去的一个重要存在。…

【解密 Kotlin 扩展函数】命名参数和默认值(十三)

导读大纲 1.0.1 命名参数1.0.2 默认参数值 上一节讲述如何自定义 joinToString 函数来代替集合的默认字符串表示 文末遗留下几个待优化问题–传送门 1.0.1 命名参数 我们要解决的第一个问题涉及函数调用的可读性 例如,请看下面的joinToString调用: joinToString(collection,&…

MyBatis深度剖析:从入门到精通的实践指南

前言 什么是mybatis&#xff1f; MyBatis是一款优秀的持久层框架&#xff0c;用于简化Java应用程序与数据库之间的交互 什么是框架&#xff0c;为什么需要框架技术&#xff1f; 框架技术 是一个应用程序的半成品提供可重用的公共结构按一定规则组织的一组组件框架优势&#x…

【自动化测试】Appium 生态工具以及Appium Desktop如何安装和使用

引言 Appium 是一个开源的自动化测试框架&#xff0c;用于测试原生、移动 Web 和混合应用程序。它支持 iOS、Android 和 Windows 平台。Appium 生态系统包含多个工具和库&#xff0c;这些工具和库可以与 Appium 一起使用&#xff0c;以提高移动应用的自动化测试效率 文章目录 引…

Java面试指南(基础篇)

文章目录 前言01 Java语言的特点02 JVM、JRE及JDK的关系03 Java和C的区别04 基本数据类型05 类型转换06 自动装箱与拆箱07 String的不可变性08 字符常量和字符串常量的区别09 字符串常量池10 String 类的常用方法11 String和StringBuffer、StringBuilder的区别12 switch 是否能…

舒服了!学大模型必看的学习书籍来了

最近整理了日前市面上一大波大模型的书&#xff0c;已经打包成pdf了&#xff0c;大家有需要的&#xff0c;可以自行添加获取&#xff0c;纯福利&#xff0c;无套路&#xff0c;添加后说明是哪本书&#xff0c;会直接给大家&#xff01;&#xff08;文末获取&#xff09; 部分书…

IO 多路转接之 epoll

文章目录 IO 多路转接之 epoll1、IO 多路转接之 poll1.1、poll 函数1.2、poll 函数返回值1.3、Socket 就绪条件1.3.1、读就绪1.3.2、写就绪1.3.3、异常就绪 1.4、poll 的优点1.5、poll 的缺点1.6、poll 改写 select 2、IO 多路转接之 epoll2.1、epoll 函数2.2、epoll_create2.3…

Leetcode 反转链表

使用递归 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ class S…

超低排放燃气锅炉

在全球环保浪潮的推动下&#xff0c;超低排放燃气锅炉以其卓越的环保性能和高效能源利用&#xff0c;正逐渐成为现代热能供应的主力军。作为传统锅炉的升级版&#xff0c;超低排放燃气锅炉不仅在技术上实现了质的飞跃&#xff0c;更在环保和节能方面树立了新的标杆。朗观视觉小…

linux入门到实操-10 控制台显示和输出重定向、监控文件变化、软连接

教程来源&#xff1a;B站视频BV1WY4y1H7d3 3天搞定Linux&#xff0c;1天搞定Shell&#xff0c;清华学神带你通关_哔哩哔哩_bilibili 整理汇总的课程内容笔记和课程资料&#xff08;包含课程同版本linux系统文件等内容&#xff09;&#xff0c;供大家学习交流下载&#xff1a;…

【Delphi】扩展现有组件创建新的 FireMonkey 组件(步骤一)

本例中演示将TLabel控件扩展成TClockLabel新控件。具体如下&#xff1a; 步骤 1 - 使用新建组件向导创建组件 1. 菜单选择 Component -> New Component。 2. 在新建组件向导的第一页&#xff0c;选择 FireMonkey for Delphi &#xff1a; 3. 在 “Ancestor Component ”页…

【最新华为OD机试E卷-支持在线评测】爱吃蟠桃的孙悟空(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…

解决windows上VMware的ubuntu虚拟机不能拷贝和共享

困扰多时的VMware虚拟机不能复制拷贝和不能看到共享文件夹的问题&#xff0c;终于解决了~ 首先确定你已经开启了复制拷贝和共享文件夹&#xff0c;并且发现不好用。。。 按照下面方式解决这个问题。 1&#xff0c;删除当前的vmware tools。 sudo apt-get remove --purge ope…

【Redis技术进阶之路】「原理分析系列开篇」揭秘分析客户端和服务端网络通信交互实现(客户端篇)

揭秘高效存储模型与数据结构底层实现 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 客户端和服务器Redis服务器IO多路复用RedisClient结构 客户端属性分析套接字描述符客户端的分类伪客户端…

【二十五】【QT开发应用】无边窗窗口鼠标拖动窗口移动,重写mousePressEvent,mouseMoveEvent函数

在 Qt 中&#xff0c;可以通过在自定义的类中重载 mousePressEvent 和 mouseMoveEvent 函数来捕获鼠标按下和移动事件&#xff0c;以便实现例如拖动窗口等功能。 mousePressEvent 和 mouseMoveEvent分别是鼠标按下事件和鼠标移动事件。这两个函数是QT中本身就存在的函数&#…

prithvi WxC气象模型

NASA发布了prithvi WxC气象模型发布 Prithvi是NASA开源的模型&#xff0c;被誉为全球最大的开源地理空间大模型。昨天晚上逛X平台&#xff0c;我看到Prithvi模型又来了新成员&#xff1a;prithvi WxC。 NASA和IBM创建了一个基于MERRA-2数据的天气和气候AI基础模型—Prithvi Wx…

C++ :借助栈完成二叉树的非递归遍历

二叉树的传统访问分为&#xff1a;前序、中序、后序、层序。 其中前三者是递归访问&#xff0c;但是递归是有缺陷的&#xff0c;树太深就会栈溢出。 因此本文我们思考如何使用非递归的方法来完成遍历。 1. 前序遍历 要迭代⾮递归实现⼆叉树前序遍历&#xff0c;⾸先还是要借…

【计算机组成原理】实验一:运算器输入锁存器数据写实验

目录 实验要求 实验目的 主要集成电路芯片及其逻辑功能 实验原理 实验内容及步骤 实验内容 思考题 实验要求 利用CP226实验箱上的K16&#xff5e;K23二进制拨动开关作为DBUS数据输入端&#xff0c;其它开关作为控制信号的输入端&#xff0c;将通过K16&#xff5e;K23设定…