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

单调栈原理

先说结论,单调栈就是维护了一个数组B,B[i-1]是B[i]的左边最近小/大位置。 且B[i]若被j pop出去,那么j就是B[i]的右边最近小/大的位置。以此得到所有位置左右最近小/最近大位置。

之前说过单调栈的使用场景和应该使用单调增还是减的栈:单调栈应用技巧-CSDN博客

这次分析下为什么单调栈可以解决这类问题。

假如要求长度为n的数组A, 在任意位置的最近小的下标。可以遍历0到n-1,遍历的同时维护一个动态长度数组B。 如果当前遍历到i, 设此时B长度为m,那么对于任意j<m, 都有:B[j-1]是B[j]的左边最近小位置。而且B[m-1]等于i-1。

这样以来,处理i时:

若A[B[m-1]]<A[i]: 那么B[m-1]一定是i最近小的位置(相邻嘛),则直接把i插到B的最后即可。

反之,我们可以得到 i最近小的位置一定<=B[m-1]的最近小的位置(此处可以用反证法),而B[m-1]最近小的位置就是B[m-2], 所以 i最近小的位置一定<=B[m-2],现在再比较A[B[m-2]]和A[i],如果还是A[B[m-2]]>=A[i],就再找B[m-3]...,直到找到A[B[m-k]]<A[i],那么B[m-k]一定是i的最近小。这些操作就等于pop掉B[m-1]到B[m-k+1]。 最后直接把i插到B的最后即可。

以上操作就维护了数组B,保证里面相邻两个数存的都是最近小的编号,换个角度看其实就是单调栈。

leetcode 84为例:

84. 柱状图中最大的矩形 - 力扣(LeetCode)

这里算下标i的贡献时:找到i的左边最近小left_less[i]和右边最近小right_less[i]。直接那么i其实能向左延伸到left_less[i]+1, 向右延伸到right_less[i]-1,贡献值为:((right_less[i]-1)-(left_less[i]+1)+1) * A[i]

代码如下

class Solution {
public:int largestRectangleArea(vector<int>& A) {stack<int> B;int n = A.size();vector<int> left_less(n);for(int i=0; i<n; i++){left_less[i] = i;while(!B.empty()&&A[B.top()]>=A[i]){B.pop();}if(!B.empty()){left_less[i] = B.top();}else{left_less[i] = -1;}B.push(i);}while(!B.empty()){B.pop();}vector<int> right_less(n);for(int i=n-1; i>=0; i--){right_less[i] = i;while(!B.empty()&&A[B.top()]>=A[i]){B.pop();}if(!B.empty()){right_less[i] = B.top();}else{right_less[i] = n;}B.push(i);}int ret= 0;for(int i=0; i<n; i++){ret = max(ret, ((right_less[i]-1)-(left_less[i]+1)+1)*A[i]);}return ret;}
};

更进一步思考,其实从左向右遍历时,假设i被j pop出去的,那么j一定是i右边的最近小(很好证明),这样就不用单独逆序遍历求right_less[i]了,直接在i被pop时记录就行了。

上代码:

class Solution {
public:int largestRectangleArea(vector<int>& A) {A.push_back(-1);stack<int> B;int n = A.size();vector<int> left_less(n);vector<int> right_less(n);for(int i=0; i<n; i++){left_less[i] = i;right_less[i] = i;while(!B.empty()&&A[B.top()]>=A[i]){right_less[B.top()] = i;B.pop();}if(!B.empty()){left_less[i] = B.top();}else{left_less[i] = -1;}B.push(i);}int ret= 0;for(int i=0; i<n; i++){ret = max(ret, ((right_less[i]-1)-(left_less[i]+1)+1)*A[i]);}return ret;}
};

相关文章:

  • 数据库系统概论-基础理论
  • 信息安全 -- 什么是侧信道攻击
  • 《[CISCN 2022 初赛]ez_usb》
  • 六级阅读---2024.12 卷一 仔细阅读1
  • C++类对象的隐式类型转换和编译器返回值优化
  • 智能货架守护者:高精度倾角传感器如何重塑仓储安全管理
  • AI恶魔之眼使用说明书
  • 注意力机制(Attention)
  • C语言指针用法详解
  • openstack虚拟机状态异常处理
  • 数据结构、刷leetcode返航版--二分【有序】5/7
  • AI开发playwright tool提示词
  • 读《暗时间》有感
  • 【C++】类和对象【下】
  • Linux 驱动开发步骤及 SPI 设备驱动移植示例
  • chili调试笔记13 工程图模块 mesh渲染 mesh共享边显示实现
  • 藏文智能输入入门实践-简单拼写纠错
  • 【Agent】使用 Python 结合 OpenAI 的 API 实现一个支持 Function Call 的程序,修改本机的 txt 文件
  • 光伏“531”政策倒逼下,光储充一体化系统如何破解分布式光伏收益困局?
  • VMware更改语言设置
  • 大风暴雨致湖南岳阳县6户房屋倒塌、100多户受损
  • 国家发改委副主任谈民营经济促进法:以法治的稳定性增强发展的确定性
  • 马上评|孩子亲近自然只能靠公园露营了吗
  • 超燃!走过莫斯科街头的“中国排面”
  • 中国德国商会报告:76%在华德企受美国关税影响,但对华投资战略依然稳固
  • 姜再冬大使会见巴基斯坦副总理兼外长达尔