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

单调栈原理

先说结论,单调栈就是维护了一个数组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;}
};

http://www.xdnf.cn/news/4573.html

相关文章:

  • vtkSmartPointer<vtkPolyData> 常用的函数方法
  • Spring Boot 多数据源事务管理
  • async/await的另一种食用方法
  • vue-quill-editor的失焦事件
  • 分布式架构详解
  • #黑马点评#(一)登录功能
  • 数字化转型-4A架构之应用架构
  • 鸿蒙编译boost
  • 浅谈微前端沙箱机制
  • 报表分析报告怎么写?零基础掌握报表分析三要素!
  • canal mysqltomysql增加同步的库操作
  • 96、数图求解(整数规划建模求解)
  • 分布式-Redis分布式锁
  • 如何用FastMCP快速开发自己的MCP Server?
  • 2024ccpc【上海+陕西】
  • Windows远程桌面实现之十七:基于浏览器的文件和目录传输(一)
  • 解决 win11 连接共享打印机,报错 0x00000709 问题
  • Analytics Service 对生产环境性能的影响
  • Spring-博客系统项目
  • 动态规划之回文串问题
  • 第7章-3 维护索引和表
  • 添加地形与自定义地形
  • HTML基础2-空元素,元素属性与页面的结构
  • livedata使用,完整的livedata的Demo
  • Spring 中org.springframework.core.Ordered接口的实战教学
  • 在 ESP-IDF 中使用 .a 静态库调用
  • 解析表观遗传学的工具——ChIP-seq(一)
  • 数据库即服务(DBaaS)领域的最新创新
  • 每日一道leetcode
  • SCADA|KingSCADA运行报错:加载实时库服务失败