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

算法(单调队列、优先队列)

引言

239. 滑动窗口最大值 - 力扣(LeetCode)

347. 前 K 个高频元素 - 力扣(LeetCode)

第一题

这一题我们将引入一个单调队列,可能大家对于单调栈并不陌生,但是单调队列的题目可能接触的比较少。单调队列解决的问题就是维护一个窗口的数据,类似于大顶堆的作用,但是并不是大顶堆。比如说大顶堆(优先队列),对于1,3,-1来说,就变成了3,1,-1,但是我们可以发现,当我们窗口移动的时候,我们需要把1弹出去,但是无论我们是从头还是尾,都无法弹出1,所以我们需要自己构造一个单调队列。我们这个队列里面只需要维护最大的元素,还是1,3,-1,当我们3进入之后,1就会自动的被我们弹出去,因为我们要求的是最大的元素,既然已经有3进来了,那么1的存在已经没有任何的意义了,所以只要有比前面大的元素进来,那么就会自动把前面那些比这个元素小的元素卷走,不需要我们在主函数里面手动调用pop()函数。这样子,我们就可以永远保证队头的元素是最大值,并且从队头到队尾,元素的大小依次减小。所以每次我们需要知道这个队列里面最大值是多少的时候,就访问队头元素即可。

我们这个队列是双端队列,也就是用deque实现的,而我们的队头是出队列的,队尾是入队列的,pop的含义就是每一次移动窗口的时候把多余的那一个元素从队头弹出去(注意:这个操作并不包含我们加入一个比之前的大的元素然后把前面那些元素弹走,原理就是我们只需要维护这个队列里面最大的元素,一定要把最大的元素放在队头)

push()函数就是加入元素,不过在加入元素之前要比较末尾的元素,为什么是末尾的元素呢?因为我们的队列是递减的,比如5,3要加入4,那么如果比较队头,就无法把3出队,而比较队尾,就是把3出队,然后把4入队,变成5,4

最后在主函数里面,我们先把队列里面的元素全部初始化好,然后窗口开始往后面移动

至于我们为什么要值传递,是因为方便我们直接操作元素,用nums[i-k]

class MyQueue { public: deque<int> que; void pop(int val) { if (!que.empty() && val == que.front()) { que.pop_front(); } } void push(int val) { while (!que.empty() && val > que.back()) { que.pop_back(); } que.push_back(val); } int getMaxValue () { return que.front(); } }; class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { MyQueue que; vector<int> res; for (int i = 0; i < k; i++) { que.push(nums[i]); } res.push_back(que.getMaxValue()); for (int i = k; i < nums.size(); i++) { que.pop(nums[i - k]); que.push(nums[i]); res.push_back(que.getMaxValue()); } return res; } };

第二题

这一题用的是优先队列来解决前k个元素的问题,优先队列的底层是大顶堆和小顶堆来实现的,也就是我们在使用优先队列的时候是需要传入我们使用的是那个堆的cmp函数。这一题主要的难点就是我们到底是使用大顶堆还是小顶堆,大顶堆的根节点是最大值,我们每一次插入结点都会把堆顶的元素弹出去,然后把新加入的元素放入堆顶,然后从根节点开始重新调整这个堆,小顶堆也是一样。所以如果我们使用的是大顶堆,我们每一次弹出去的是最大频率的那一个,最后维护了k个最小的频率元素。所以我们应该使用小顶堆

class Solution { public: class cmp { public: bool operator()(const pair<int, int>& a, const pair<int, int>& b) { return a.second > b.second; } }; vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> umap; for(int i = 0; i < nums.size(); i++) { umap[nums[i]]++; } priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> que; for(auto iter = umap.begin(); iter != umap.end(); iter++) { que.push(*iter); if (que.size() > k) { que.pop(); } } vector<int> res(k, 0); for (int i = k - 1; i >= 0; i--) { res[i] = que.top().first; que.pop(); } return res; } };

对于堆的调整代码,我这里也给出来了:这里传递的i就是1,因为是堆顶,然后不断的向下调整

void DownAdjust(int a[], int i, int n) { // 向下调整 int now = i; // 当前结点 int next; // 值最大的孩子 while(2 * now <= n) { // now的左子树存在,也就是说当前结点至少有一个孩子 next = 2 * now; if(2 * now + 1 <= n && a[2 * now + 1] > a[next]) { // 右子树存在,且右孩子值更大 next = 2 * now + 1; } if(a[now] < a[next]) { // 父亲比孩子小 std::swap(a[now], a[next]); now = next; // 再继续向下调整 }else { // 如果父亲比孩子大,说明已经是大顶堆了,不需要调整了 break; } } }
http://www.jsqmd.com/news/1080903/

相关文章:

  • lessmsi技术深度解析:Windows Installer文件逆向工程与提取架构设计
  • LLM聊天机器人质量评估实战指南:从幻觉检测到多轮状态追踪
  • 从零构建802.15.4星型网络:MAC层实现与低功耗设计详解
  • 显卡驱动冲突,GPU直通失效,vSphere渲染中断——VMware黑屏三大隐性杀手全拆解
  • SAP RFC Adapter 调试属性深解,从 Payload 切片到 Server Listener Trace 的排障思路
  • DPAA2架构解析:硬件加速与Linux驱动集成实战
  • 拳皇2002冰蓝版手机版下载
  • MCP1631 PWM控制器:智能电源与电池充电系统设计实战
  • 5分钟掌握8球台球辅助工具:提升瞄准精度的终极指南
  • 深入MPC8315E寄存器配置:DDR与PCIe控制器实战解析
  • Copier 总报错?一篇讲透排查、升级、治理和团队落地
  • 2026华米与佳明旗舰运动手表大比拼:谁更省钱又物有所值?
  • 深入解析FlexPWM高级功能:输入捕获、死区控制与故障保护实战
  • 基于MCP16251/2的单节电池高效升压电路设计与UVLO保护实现
  • 嵌入式本地总线控制器(LBC)原理与实战:以MPC8315E eLBC驱动NAND Flash为例
  • MPC8315E硬件加密引擎解析:嵌入式网络安全与性能的平衡之道
  • YouTube Shorts 新增清屏、2 倍速等功能,加速追赶 TikTok!
  • 嵌入式开发中Pragma指令的深度解析与实战应用
  • 2026年私人音乐厅打造,揭秘全球**声学品牌声场技术天差地别
  • MCP1631智能电源设计:从降压控制器到电池充电系统实战
  • S12SDBGV2硬件调试模块:追踪缓冲区与状态机断点实战解析
  • Agentic LLM应用可靠性评测:四维行为测试体系实战指南
  • 网络处理器内核服务:事件定时器、上下文管理与同步机制深度解析
  • FPGA加速实时语义分割:低功耗LMIINet部署实践
  • 第 8 篇:Cookie 与 Session:登录态的本质
  • Jellyfin中文影视刮削终极指南:MetaShark插件完整配置教程
  • MC9S12HY PIM模块实战:引脚复用、寄存器配置与调试指南
  • 布帘面料选型全解析:雪尼尔 / 高精密 / 棉麻 / 绒布 / 亚麻性能对比与工程化配置方案
  • 如何快速掌握Android虚拟定位:无需Root的终极解决方案
  • 【2027最新】基于SpringBoot+Vue的PS游戏服务网站管理系统源码+MyBatis+MySQL