【基础算法总结】优先级队列

优先级队列

  • 1.最后一块石头的重量
  • 2.数据流中的第 K 大元素
  • 4.前K个高频单词
  • 4.数据流的中位数

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.最后一块石头的重量

题目链接:1046. 最后一块石头的重量

题目分析:

在这里插入图片描述

每一回合,从中选出两块 最重的 石头,x == y,那么两块石头都会被完全粉碎;如果 x != y,那么重量较小的 x 石头将会完全粉碎,而重量较大的 y 的石头新重量为 y-x。最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

在这里插入图片描述

算法原理:

每次挑选的是先挑一堆数中最大的那个数,然后再挑一个剩下数中最大的数。这不正好符合大根堆的数据结构吗。

解法:用堆来模拟这个过程

先拿数组的数创建一个大根堆,每次从堆里面选择最大和次大两个数,如果相等就是0不用加入到大根堆里,如果不相等用最大的数减去次大的数,把结果加入到堆里面。如果最后堆里还剩下一个数,返回这个数。如果堆为空说明石头全都粉碎了返回0即可。

class Solution {
public:int lastStoneWeight(vector<int>& stones) {//1.拿元素创建一个大根堆priority_queue<int> heap(stones.begin(),stones.end());//2.模拟这个过程while(heap.size() >= 2){int s1 = heap.top();heap.pop();int s2 = heap.top();heap.pop();if(s1 != s2) heap.push(s1 - s2);}return heap.size() ? heap.top() : 0;}
};

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

题目链接:703. 数据流中的第 K 大元素

题目分析:

在这里插入图片描述
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

在这里插入图片描述
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

在这里插入图片描述

算法原理:

这道题其实考察的是一个非常经典的问题, TopK问题。
关于TopK问题有两种解题思路,一种是堆,一种是前面刚学的快速选择算法。快速选择排序虽然快,但是对于海量的数据内存根本放不下。所以在海量数据情况最优的还是堆。

在这里插入图片描述

解法:用 堆 来解决

  1. 创建一个大小为 k 的堆(大根堆 or 小根堆)
  2. 循环
    1.依次进堆
    2.判断堆的大小是否超过 K

在堆的实现,画图和代码分析建堆,堆排序,时间复杂度以及TOP-K问题,对于求第K个最大元素,我们也是将前K个数建个小堆,然后将剩下的N-K个元素和堆顶元素做比较,如果大于堆顶元素,就先把栈顶元素pop掉,也就是把堆中最小元素删除,然后将它放进去。这样一直循环,直到所有元素都比较完成。因为是一直把堆中最小的pop掉,那堆的元素就是N个元素中最大的,而堆顶就是第K个最大的元素,别忘记我们这是一个小堆!

这里我们也是建小堆,依次进堆,当堆大小超过K个,pop堆顶元素,把堆中最小元素pop掉,并且使堆保持K个大小。每次都是堆中最小元素去掉。那最后堆中剩下的就是前K个最大元素,而堆顶就是第K个最大元素。

考虑一下下面两个问题

  1. 用大根堆还是小根堆
  2. 为什么要用大根堆(小根堆)
class KthLargest {
public:priority_queue<int,vector<int>,greater<int>> heap;int _k;KthLargest(int k, vector<int>& nums) {_k = k;for(auto& x : nums){heap.push(x);if(heap.size() > k) heap.pop();}}int add(int val) {heap.push(val);if(heap.size() > _k) heap.pop();return heap.top();}
};

4.前K个高频单词

题目链接:692. 前K个高频单词

题目分析:

在这里插入图片描述

这是一个TopK的问题,注意,返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序(由低向高) 排序。

算法原理:

解法:利用 “堆” 来解决 TopK 问题

  1. 预处理一下原始的字符串数组: 用一个哈希表,统计一下每一个单词出现的频次。

  2. 创建一个大小为 k 的堆,类提供的比较函数满足不了要求,我们要自己定义一个!(返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序(由低向高) 排序。)如果比较频次创建一个小根堆,如果比较字典序(频次相同的时候)创建一个大根堆。所以说创建堆写比较函数的时候必须要考虑这两点,当频次相同的时候字典序按照大根堆方式比较,当频次不同的时候按照小根堆方式比较。

  3. 循环
    1.依次进堆
    2.判断堆的大小是否超过 K

  4. 提取结果

因为求前K大,所以建的是一个小根堆,然后提取堆顶元素在pop是一个升序的。有两种方式拿最终答案,要么逆序一下取前K个,要么从后往前取K个。

class Solution {
public:struct Compare{bool operator()(const pair<string,int>& l,const pair<string,int>& r){//频次不同按照小根堆比较, 频次相同字典序按照大根堆方式比较return l.second > r.second || (l.second == r.second && l.first < r.first);}};vector<string> topKFrequent(vector<string>& words, int k) {vector<string> ret;// 1.统计一下每一个单词的频次map<string,int> count;for(auto& str : words){count[str]++;}// 2.创建一个大小为 k 的小堆priority_queue<pair<string,int>,vector<pair<string,int>>,Compare> heap;// 3.TopK 的主逻辑for(auto& m : count){heap.push(m);if(heap.size() > k) heap.pop();}// 4.提取结果vector<string> tmp;while(!heap.empty()){tmp.push_back(heap.top().first);heap.pop();}reverse(tmp.begin(),tmp.end());for(int i = 0; i < k; ++i)ret.push_back(tmp[i]);return ret;}
};

4.数据流的中位数

题目链接:295. 数据流的中位数

题目分析:

在这里插入图片描述

给一个数据流,让返回每次当前已经加入到数组的数据流的中位数。中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。表的大小是奇数,直接返回中间的数。

在这里插入图片描述

算法原理:

解法一:直接sort

每次从数据流中来一个数插入数组中之后,都对当前数组进行sort,然后通过下标的方式找到中间数。

在这里插入图片描述

每次add加入一个数都要sort,时间复杂度是O(nlogn)。总体时间复杂度是非常恐怖的。因为是下标访问,find 时间复杂度是 O(1)

解法二:插入排序的思想

[0,end] 有序,插入 end + 1,使 [0, end + 1]有序。这道题正好就是这样的思想。

在这里插入图片描述
add函数,每次插入一个数的时候都需要从后往前扫描找一个插入的位置,因此时间复杂度是O(n),find 也是通过下标去找 时间复杂度是O(1)

解法三:大小堆来维护数据流的中位数

此时有一个数轴已经按照从小到大的排好序了,这个时候想找中间数的时候。把这些数的前半部分放到一个大根堆里面,后半部分放到小根堆里面。此时找中间值也很快,前面较小的数放到大根堆里面,堆顶元素是数轴中这些较小元素种最右边的值。后面较大的数放到小根堆里面,堆顶元素是数轴中这些较大元素最左边。此时我们仅需根据数组中的元素的个数就可以求出中位数是多少了。如果数组是偶数个大根堆和小根堆正好把数轴平分,然后大堆堆顶元素和小堆堆顶元素相加/2就是这个数组的中位数。如果数组是奇数个。我们就先人为规定一下,数轴左边元素是m个,右边是n个,要么 m == n,要么 m > n (n = n + 1)。如果 m == n 正好平均分。如果数轴个数是奇数个,人为规定左边大根堆多方一个元素,m > n(n = n + 1),此时中位数就是左边大根堆的堆顶元素。

在这里插入图片描述
向这样用大根堆存左边较小部分,小根堆存右边较大部分。find 时间复杂度也是O(1),而add快了很多,因为我们是从堆存这些元素的,插入和删除每次调整堆仅需O(logn)

细节问题:

add如何实现:
假设现在有两个堆了。一个大根堆left,一个小根堆right。left元素个数m个,right元素个数n个,left堆顶元素x,right堆定元素y。如果此时来了一个数num,num要么放在left里,要么放在right里。但是放好之后可能会破坏之前的规则:

  1. m == n
  2. m > n —> m == n + 1

我们必须维护上面的规则,才能正确找到数组中位数。
接下来分情况讨论:

  1. m == n

num要么插入left,要么插入right。

如果num要进入left,那么num <= x,但是别忘记 m == n 有可能两个堆全为空,num也是直接进入left。此时 m 比 n 多了一个。没关系直接进就行。

如果num进入right,那条件是 num > x。此时就有问题了。n > m了,而 n > m是不被允许的,所以我们要把右边堆调整一下,就是拿right堆顶元素放到left里。因为right是一个小根堆,堆顶就是最小值。拿到left,还是能保证left堆里元素是较小的,right堆里元素是较大的。拿right堆顶元素放到left里正好满足 m == n + 1。这里有一个细节问题,必须num先进right堆,然后再拿right堆定元素放到left,因为 x < num < y,如果直接把y拿过去了,就破坏了left都是较小元素。right都是较大元素。

在这里插入图片描述

  1. m > n —> m == n + 1

如果num进入left,那么num <= x , 但是此时不满足 m == n + 1,因此 进栈后将栈顶元素给right。

如果num进入right,那么num > x , m == n了,直接进就行了

在这里插入图片描述

class MedianFinder {
public:MedianFinder() {}void addNum(int num) {// 分类讨论即可int m = left.size(), n = right.size();if(m == n) // 左右两个堆的元素个数相同{if(m == 0 || num <= left.top())left.push(num);else{right.push(num);left.push(right.top());right.pop();}}else //左边堆的元素个数比右边堆元素个数多 1{if(num <= left.top()){left.push(num);right.push(left.top());left.pop();}elseright.push(num);}}double findMedian() {int m = left.size(), n = right.size();if(m == n) return (left.top() + right.top()) / 2.0;else return left.top();}private: priority_queue<int> left;priority_queue<int,vector<int>,greater<int>> right;
};

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

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

相关文章

FPGA开发——LED流水灯实现先从左往右流水,再从右往左流水

一、概述 我们在设计完一个方向的流水灯的设计时&#xff0c;总是会想实现让流水灯倒着流水回去的设计&#xff0c;这里我也是一样&#xff0c;实现这种设计的方法有很多种&#xff0c;其中就有直接使用case语句将所有可能包含进去编写&#xff0c;这种设计方法是最简单的&…

leetcode日记(51)不同路径Ⅱ

和上一道题&#xff08;无障碍物的最短路径&#xff09;很像&#xff0c;但事实上比上一题多了优化方法 根据上一题改的代码如下&#xff0c;添加了对障碍物的判定&#xff0c;如果有障碍物则将数组值设为0。 class Solution { public:int uniquePathsWithObstacles(vector&l…

Origin制作线性拟合回归图

选中数据&#xff0c;点下方散点图 调整散点颜色 在分析中打开线性拟合回归 添加文本 显示上轴

算法 —— 暴力枚举

目录 循环枚举 P2241 统计方形&#xff08;数据加强版&#xff09; P2089 烤鸡 P1618 三连击&#xff08;升级版&#xff09; 子集枚举 P1036 [NOIP2002 普及组] 选数 P1157 组合的输出 排列枚举 P1706 全排列问题 P1088 [NOIP2004 普及组] 火星人 循环枚举 顾名思…

keil调试SH79F7416

仿真器JET51A, 调试设置 选择器件 再次点击调试就一切正常啦

快速汇总公司产品涉及的项目(服务、站点)

文章目录 引言I 快速汇总公司产品涉及的项目II 常用工具jar包转成exe应用远程操作常用命令III 把应用做成windows服务在后台运行借助工具`instsrv.exe`和`srvany.exe`把应用做成windows服务的步骤SysWOW64 文件夹的作用引言 需求:汇总 平台涉及站点和服务信息 I 快速汇总公司…

SkyWalking入门搭建【apache-skywalking-apm-10.0.0】

Java学习文档 视频讲解 文章目录 一、准备二、服务启动2-1、Nacos启动2-2、SkyWalking服务端启动2-3、SkyWalking控制台启动2-4、自定义服务接入 SkyWalking 三、常用监控3-1、服务请求通过率3-2、服务请求拓扑图3-3、链路 四、日志配置五、性能剖析六、数据持久化6-1、MySQL持…

MySQL SQL 编程练习

目录 创建表并插入数据 查看表结构 创建触发器 创建INSERT 触发器 创建DELETE 触发器 创建更新触发器 创建存储过程 创建提取emp_new表所有员工姓名和工资的存储过程s1 创建存储过程s2&#xff0c;实现输入员工姓名后返回员工的年龄 创建一个存储过程s3&#xff0c;有2个参数&…

Pytorch使用教学5-视图view与reshape的区别

有同学后台留言问为什么view有时可对张量进行形变操作&#xff0c;有时就会报错&#xff1f;另外它和reshape功能好像一致&#xff0c;有什么区别呢&#xff1f;本文就带你了解PyTorch中视图的概念。 在PyTorch中对张量进行形变操作时&#xff0c;很多同学也会使用view方法&am…

3.2、数据结构-数组、矩阵和广义表

数组结构 数组是定长线性表在维度上的扩展,即线性表中的元素又是一个线性表。N维数组是一种“同构”的数据结构,其每个数据元素类型相同、结构一致。 一个m行n列的数组表示如下: 其可以表示为行向量形式&#xff08;一行一行的数据&#xff09;或者列向量形式&#xff08;一…

Windows搭建Nginx代理本地盘的文件 共享本地文件

一、查询自己的内网IP和外网IP的方法&#xff0c;以及判断是否直接连接到公网 内网IP&#xff0c;即局域网IP&#xff1a; 打开cmd窗口&#xff0c; 输入 ipconfig 后回车 外网IP&#xff0c;即公网IP&#xff1a; 打开cmd窗口&#xff0c;输入curl ifconfig.me指令访问ifconfi…

PE文件(十二)导入表

导入表 导入表的引入 当一个PE文件&#xff08;如.dll/.exe等&#xff09;需要使用别的模块的函数&#xff0c;也叫做依赖某模块&#xff0c;就需要一个清单来记录使用的模块&#xff08;一般为.dll文件&#xff0c;为方便理解&#xff0c;以后我们将模块都认为是.dll文件&am…

Python写UI自动化--playwright(通过UI文本匹配实现定位)

本篇简单拓展一下元素定位技巧&#xff0c;通过UI界面的文本去实现定位 目录 匹配XPath 匹配文本元素 .count()统计匹配数量 处理匹配文本返回多个元素 1、使用.nth(index)选择特定元素: 2、获取所有匹配的元素并遍历: 3、错误处理: 匹配XPath 比如我们要定位到下图的…

VScode连接虚拟机运行Python文件的方法

声明&#xff1a;本文使用Linux发行版本为rocky_9.4 目录 1. 在rocky_9.4最小安装的系统中&#xff0c;默认是没有tar工具的&#xff0c;因此&#xff0c;要先下载tar工具 2. 在安装好的vscode中下载ssh远程插件工具 3. 然后连接虚拟机 4. 查看python是否已经安装 5. 下载…

Linux网络:传输层协议TCP(一)

目录 一、TCP协议的定义 二、确认应答机制ACK 三、序号、确认序号 四、超时重传机制 一、TCP协议的定义 TCP 全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传 输进行一个详细的控制; TCP 协议段格式 • 源/目的端口号: 表示数据…

减轻幻觉新SOTA,7B模型自迭代训练效果超越GPT-4,上海AI lab发布

LLMs在回答各种复杂问题时&#xff0c;有时会“胡言乱语”&#xff0c;产生所谓的幻觉。解决这一问题的初始步骤就是创建高质量幻觉数据集训练模型以帮助检测、缓解幻觉。 但现有的幻觉标注数据集&#xff0c;因为领域窄、数量少&#xff0c;加上制作成本高、标注人员水平不一…

vue3前端开发-小兔鲜项目-登录和非登录状态下的模板适配

vue3前端开发-小兔鲜项目-登录和非登录状态下的模板适配&#xff01;有了上次的内容铺垫&#xff0c;我们可以根据用户的token来判定&#xff0c;到底是显示什么内容了。 1&#xff1a;我们在对应的导航组件内修改完善一下内容即可。 <script setup> import { useUserSt…

抖音直播弹幕数据逆向:websocket和JS注入

&#x1f50d; 思路与步骤详解 &#x1f575;️‍♂️ 思路介绍 首先&#xff0c;我们通过抓包工具进入的直播间&#xff0c;捕获其网络通信数据&#xff0c;重点关注WebSocket连接。发现直播弹幕数据通过WebSocket传输&#xff0c;这种方式比传统的HTTP更适合实时数据的传输。…

细说MCU用定时器控制单路DAC模块设计和输出锯齿波的实现方法

目录 一、参考工程 二、仅提供不同的配置 1、用定时器控制DAC输出 2、配置定时器参数 三、代码修改 四、 运行并观察显示效果 一、参考工程 本工程依赖作者的文章&#xff1a;细说MCU用单路DAC模块设计和输出锯齿波的实现方法-CSDN博客 https://wenchm.blog.csdn.net/ar…

Mamba-yolo|结合Mamba注意力机制的视觉检测

一、本文介绍 PDF地址&#xff1a;https://arxiv.org/pdf/2405.16605v1 代码地址&#xff1a;GitHub - LeapLabTHU/MLLA: Official repository of MLLA Demystify Mamba in Vision: A Linear AttentionPerspective一文中引入Baseline Mamba&#xff0c;指明Mamba在处理各种高…