【算法】使用优先级队列(堆)解决算法题(TopK等)(C++)

文章目录

  • 1. 前言
  • 2. 算法题
    • 1046.最后一块石头的重量
    • 703.数据流中的第K大元素
  • 2.5 如何选择大根堆 与 小根堆? + 为什么选择大根堆(小根堆)?
    • 692.前K个高频单词
    • 295.数据流的中位数

1. 前言

我们知道:优先级队列是一种常用的数据结构,用于解决许多算法问题。基于堆(Heap)实现,在每次操作中能够快速找到最大或最小值

使用优先级队列的典型算法问题包括:

  • Top K 问题:查找列表中前 K 个最大或最小的元素。
  • 合并 K 个排序数组:将 K 个已排序的数组合并为一个有序数组。
  • Dijkstra 算法:在加权图中找到从起点到目标节点的最短路径。
  • Huffman 编码:使用最小堆构建前缀编码树来压缩数据。

下面会挑选一些算法题并使用优先级队列进行解题。


2. 算法题

1046.最后一块石头的重量

在这里插入图片描述
思路

  • 解法大根堆
  • 大根堆:每个节点都大于等于其子节点,则堆顶节点为最大的
    1. 将数组中所有元素加入堆中,并进行循环,直至堆为空
    2. 循环每次 取两次堆顶元素,即当前最重的两石头
    3. 将两元素差继续入堆,重复过程直至循环结束
      • 如果堆中还剩一个元素,返回该元素
      • 如果已经没有元素,返回0

代码

int lastStoneWeight(vector<int>& stones) {priority_queue<int> heap; // 创建大根堆// 将数组所有元素添加到堆中for(int stone : stones) heap.push(stone);while(heap.size() > 1){// 每次取最大的两个数int a = heap.top(); heap.pop();int b = heap.top(); heap.pop();if(a > b) heap.push(a - b);}return heap.size() ? heap.top() : 0;
}

703.数据流中的第K大元素

在这里插入图片描述

思路

  • 题意分析:根据题目,可以看出来该题是一道topK类型题
  • 解法全局变量 + 小根堆
    • 使用全局变量可以省去函数之间传参的过程,也方便编写代码
    1. 创建全局变量标记k和创建全局小根堆
      • 关于为什么选择小根堆,可以看后面的解释。
    2. 由于add函数要求添加数字后返回第k大的元素,对于构造函数KthLargest,我们直接将数组中前k大的元素插入
    3. 对于add函数,直接将val插入到堆中并判断是否堆内元素超出k个
      • 如果超出,则pop掉,后直接返回堆顶元素(即为第K大)

2.5 如何选择大根堆 与 小根堆? + 为什么选择大根堆(小根堆)?

在这里插入图片描述

代码

class KthLargest {
public:// 小根堆priority_queue<int, vector<int>, greater<int>> heap;int _k;KthLargest(int k, vector<int>& nums) {_k = k;for(int num : nums){heap.push(num);if(heap.size() > _k) heap.pop();} }int add(int val) {heap.push(val);if(heap.size() > _k) heap.pop();return heap.top();}
};

692.前K个高频单词

在这里插入图片描述

思路

  • 题意分析:即返回数组中出现次数最多的字符串(单词)
  • 解法哈希表 + 优先级队列
    1. 哈希表统计每个单词的出现次数
    2. 根据题目要求,当单词的频率相同时,按照字典序排列,则我们自定义优先级队列的比较函数。则:
      • 当单词频率不同时,用小堆的比较方式
      • 当单词频率相同时,按照字典序,用大堆的比较方式
    3. 将哈希表中统计的前k高的 字母以及频率 加入到队列
    4. 最后返回结果,遍历堆,每次加入到结果集result中并pop即可。

代码

vector<string> topKFrequent(vector<string>& words, int k) {// 统计单词出现频率unordered_map<string, int> freq;for (const string& word : words) {freq[word]++;}// 自定义优先队列的比较函数auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {// 比较出现次数,如果相同则按照字母顺序return a.second > b.second || (a.second == b.second && a.first < b.first);};// 优先队列,默认是大顶堆,用于存储频率最高的 k 个单词priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> pq(cmp);// 遍历统计好的频率,将单词加入优先队列for (const auto& entry : freq) {pq.push(entry);if (pq.size() > k) {pq.pop(); // 如果队列大小超过 k,则弹出频率最小的单词}}// 从优先队列中取出结果vector<string> result(k);for (int i = k - 1; i >= 0; --i) {result[i] = pq.top().first; // 逆序存储结果pq.pop();}return result;
}

295.数据流的中位数

在这里插入图片描述

思路

  • 题意分析:题目要求实现一个类,类中包含一个构造函数、一个add函数用于添加元素、以及一个find函数

  • 解法一排序 sort
    在这里插入图片描述

    • 对于本题,使用该排序法是会超时的
  • 解法二插入排序的思想
    在这里插入图片描述

    • 插入排序思想解本题是有可能超时的,但依然需要了解这种解题思想。
  • 解法三大小堆维护
    在这里插入图片描述
    在这里插入图片描述

    • 上图解释了方法思路,具体细节看下面代码即可。

代码

class MedianFinder {
public:// 大小堆,左大堆,右小堆// 且当共有奇数个元素时,左存多一个元素priority_queue<int, vector<int>> left;priority_queue<int, vector<int>, greater<int>> right;MedianFinder() {} // 构造void addNum(int num) {if(left.size() == right.size()){   if(left.empty() || num <= left.top()){left.push(num);}else{right.push(num);left.push(right.top());right.pop();}}else{if(num <= left.top()){left.push(num);right.push(left.top());left.pop();}else{right.push(num);}}}double findMedian() {return (left.size() == right.size()) ? (left.top() + right.top()) / 2.0 : left.top();}
};

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

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

相关文章

神器yakit之web fuzzer功能

前言 yakit并不像burp一样单独设置爆破模块&#xff0c;但是yakit也是可以爆破的&#xff0c;并且更好用&#xff08;个人感觉&#xff09;。 手工测试场景中需要渗透人员对报文进行反复的发送畸形或者特定的payload进行查看服务器的反馈并以此来进行下一步的判断。 Fuzz标签便…

C#MQTT编程08--MQTT服务器和客户端(cmd版)

1、前言 前面完成了winform版&#xff0c;wpf版&#xff0c;为什么要搞个cmd版&#xff0c;因为前面介绍了mqtt的报文结构&#xff0c;重点分析了【连接报文】&#xff0c;【订阅报文】&#xff0c;【发布报文】&#xff0c;这节就要就看看实际报文是怎么组装的&#xff0c;这…

016-Vue-黑马2023:前后端分离开发(在线接口文档),前端工程化、Element、vue编写一个完成页面、Vue路由、vue打包部署到nginx

第三节 前后端分离开发 1、介绍 开发模式 前后端混合开发&#xff1a;传统开发模式 前后端分离开发&#xff1a;当前最为主流的开发模式 页面原型需求案例&#xff1a;分析出接口文档 离线开发文档示例&#xff1a; 2、YAPI&#xff08;官网已停用&#xff09; 202…

手机改直供电移除电池后使用改充电器供电改充电宝供电反复重启无法启动进入界面后重启黑屏

手机改直供电移除电池后使用改充电器供电改充电宝供电反复重启无法启动进入界面后重启黑屏 手机直供电改造拆电池问题接二连三无法连接电脑手机突然关机&#xff0c;反复重启 供电的解决方案新的希望 安装米家APP总结 手机直供电改造 首先我还是要声明一下&#xff0c;我非专业…

php反序列化之pop链构造(基于重庆橙子科技靶场)

常见魔术方法的触发 __construct() //创建类对象时调用 __destruct() //对象被销毁时触发 __call() //在对象中调用不可访问的方法时触发 __callStatic() //在静态方式中调用不可访问的方法时触发 __get() //调用类中不存在变量时触发&#xff08;找有连续箭头的…

cusor编程IDE + 配置gpt3.5 api

1、购买gpt3.5的api 可以使用这个 https://www.mailline.org?from1726 也可以自己想办法去官网获取 如果是mailline&#xff0c;需要选择第一个&#xff0c;可以先买一个看看&#xff0c;我每次都是买10个&#xff0c;一个0.8元&#xff0c;有5美刀的额度。 下单后获取到ke…

Windows电脑桌面便利贴,桌面便签记事本怎么设置?

在繁忙的工作中&#xff0c;因为有很多重要的事需要等忙完手头上的事后或者明后天才能去做&#xff0c;但又怕忘记&#xff0c;经常会用便利贴先记录下来。对于Windows电脑桌面便利贴&#xff0c;不仅需要随时可以记录图文内容&#xff0c;还要能添加待办清单&#xff0c;设置提…

MyBatis 使用报错:org.xml.sax.SAXParseException 元素内容必须由格式正确的字符数据或标记组成

文章目录 前言问题分析解决方案方案一&#xff1a;使用 CDATA 区块&#xff0c;依然使用 “ > ” 或者 “ < ”方案二&#xff1a;使用转义字符 个人简介 前言 今天在使用 MyBatis 时出现报错&#xff1a; Caused by: org.xml.sax.SAXParseException: 元素内容必须由格式…

Git教程学习:01 Git简介与安装

目录 1 版本控制1.1 什么是版本控制系统&#xff1f;1.2 本地版本控制系统1.3 集中式版本控制系统1.4 分布式版本控制系统 2 Git简史3 Git的安装3.1 在Linux上安装3.2 初次运行Git前的配置 1 版本控制 1.1 什么是版本控制系统&#xff1f; 版本控制系统(Version Control Syst…

Gin 框架之Cookie与Session

文章目录 一、Cookie和Session的由来二、Cookie简介1. 什么是Cookie2. Cookie规范3. 安全性4. Cookie 关键配置 三、Session简介1. 什么是Session2. Session 安全性3. 如何让客户端携带 sess_id 四、使用 Gin 的 Session 插件4.1 介绍4.2 基本使用 五、 session与store5.1 会话…

面对根据角色和单子状态如何有效的进行按钮权限的控制

当阁下看到这个按钮权限控制时&#xff0c;该如何应对 按钮权限是根据工单流程状态进行的&#xff0c;当工单走到某个流程时就显示该表格中对应的状态&#xff0c;初看也就简简单单&#xff0c;仔细一看&#xff0c;逻辑还是很复杂&#xff0c; 首先这里得说一下背景&#xff…

2024--Django平台开发-订单项目管理(十四)

day14 订单管理系统 1.关于登录 1.1 UI美化 页面美化&#xff0c;用BootStrap 自定义BooStrapForm类实现。 class BootStrapForm:exclude_filed_list []def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# {title:对象,"percent":对象}fo…