刷题笔记:力扣第84题-柱状图中最大的矩形

1.本题和力扣第42题-接雨水的单调栈方法是遥相呼应的,那道题目是使用递增单调栈,寻找凹槽,本道题就是使用递减单调栈,寻找凸起。

2.一开始的想法如下:

(1)遍历一遍数组,每次都将存储的结果与当前元素大小比较,避免出现元素本身形成矩形即为答案的情况。

(2)先寻找两边低中间高的凸起结构,处理方式类似于接雨水题目,矩形宽度w等于当前下标i减去栈顶下标,计算的时候需要考虑两种情况:较高边为高还是较低边为高。若较高边为高,宽度应为w -- 1,;若较低边为高,宽度即为w。

(3)遍历完一遍数组后,处理剩余的栈内元素,此时栈内元素一定是非递增的,所以依次算出所有可能的矩形面积后与结果值进行比较即可。

3.基于以上思想,写出的完整代码如下:

cpp 复制代码
 1. int largestRectangleArea(int* heights, int heightsSize) {
 2.     int stack[100001];
 3.     int top = 0;
 4.     int res = 0;
 5.  
 6.     for (int i = 0; i < heightsSize; i++){
 7.         res = fmax(res, heights[i]);
 8.         while (top >= 1 && heights[stack[top - 1]] > heights[i]){
 9.             top--;
10.             if (top >= 1){
11.                 int w = i - stack[top - 1] + 1;
12.                 int res1 = fmin(heights[i], heights[stack[top - 1]]) * w;
13.                 int res2 = fmax(heights[i], heights[stack[top - 1]]) * (w - 1);
14.                 int res_tmp = fmax(res1, res2);
15.                 res = fmax(res, res_tmp);
16.             }
17.         }
18.         stack[top++] = i;
19.     }
20.  
21.     for (int i = 0; i < top; i++){
22.         res = fmax(res, heights[stack[i]] * (top - i));
23.     }
24.  
25.     return res;
26. }

本地测试通过了,但是提交后出错了:

经过检查,发现自己处理栈内剩余元素的逻辑是错误的,虽然剩余元素确实是非递增的,但是最终的答案可能贯穿了已经出栈的元素和栈内剩余元素。比如该测试用例中,元素1就是如此,最终答案应该是1贯穿了全部元素,答案为1*3=3。

4.本题使用递减单调栈的核心思想没有错,但代码实现逻辑有问题,本题应该是通过遍历数组来求中间凸起部分的最大面积,而不是带上两边的最大面积,这样会导致情况复杂且最后无法处理剩余栈元素。且本题需要引入虚拟节点的思想,在数组首尾各加一个虚拟的0,首部的0可以处理栈空的情况,此时矩形宽度就是当前元素的下标;尾部的0可以处理最后栈有剩余的情况,强制所有元素出栈。

5.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. int largestRectangleArea(int* heights, int heightsSize) {
 2.     // 单调栈,存储柱子下标,栈内下标对应的高度保持单调递增
 3.     int stack[100001];
 4.     // top=0 代表栈为空
 5.     int top = 0;
 6.     // 记录最大矩形面积
 7.     int res = 0;
 8.     // 遍历到 i == heightsSize 时,cur_h置0,相当于在数组末尾追加一根高度为0的柱子,用来强制弹出栈中剩余所有元素
 9.     for (int i = 0; i <= heightsSize; i++){
10.         // 到达数组末尾,当前高度取0;否则取heights[i]
11.         int cur_h = (i == heightsSize) ? 0 : heights[i];
12.         // 栈不为空,且栈顶柱子高度大于当前高度,说明找到栈顶柱子的右边界
13.         while (top >= 1 && heights[stack[top - 1]] > cur_h){
14.             // 取出要计算面积的柱子高度
15.             int h = heights[stack[top - 1]];
16.             // 弹出栈顶下标
17.             top--;
18.             
19.             // 宽度:栈空说明左侧没有比它更小的柱子,宽度就是i;否则宽度=当前i - 新栈顶下标 -1
20.             int w = (top == 0) ? i : (i - stack[top - 1] - 1);
21.             int area = h * w;
22.             // 更新最大面积
23.             res = fmax(res, area);
24.         }
25.         // 当前下标入栈,维持单调递增栈
26.         stack[top++] = i;
27.     }
28.     return res;
29. }

该算法时间复杂度和空间复杂度均为O(n)。

相关推荐
欣欣之王来了25 分钟前
AI合规专项:AI算法透明度的合规要求
人工智能·算法
wzdark26 分钟前
分块思想在算法优化中的实践与应用4
算法
Because_of_Her134 分钟前
dfs-洪水填充技巧
笔记·算法·深度优先·洪水填充
小静AI工程实验室1 小时前
RSA 算法详解:从模幂原理到 OAEP 加密、PSS 签名与 Python 实验
python·算法
xzal121 小时前
GESP Python笔记
笔记·python·算法
wzdark1 小时前
算法中的记忆化思想与重复子问题优化4
算法
Discipline10291 小时前
流水线运行是否正随时间变得更加集中?
算法
周玉奎先生1 小时前
CGM系列水泥基灌浆料型号参数解析及安庆重点工程应用匹配
经验分享·笔记·零售
海海不掉头发1 小时前
软件项目管理学习笔记-从软件危机到敏捷与DevOps
笔记·学习·devops
lzhdim2 小时前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#