单调栈小结
单调栈小结
刷Leetcode的时候遇到了一些求柱状面积之类的题目,可以使用单调栈的方法进行求解。单调栈的定义为栈中存放的数据出栈应该是有序的所以单调栈也分为单调递增栈和单调递减栈
- 单调递增栈:栈中数据出栈的序列为单调递增序列
- 单调递减栈:栈中数据出栈的序列为单调递减序列
例子:现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。
10入栈时,栈为空,直接入栈,栈内元素为10。
3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。
具体的内容可以参考链接:https://blog.csdn.net/lucky52529/article/details/89155694
接雨水
题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
这题的解题方法还挺多种多样的,暴力、动态规划、双指针,为了切题我们先展示单调栈的做法(虽然我一个都不会),参考题解的是https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
public int trap6(int[] height) {
int sum = 0;
Stack<Integer> stack = new Stack<>();
int current = 0;
while (current < height.length) {
//如果栈不空并且当前指向的高度大于栈顶高度就一直循环
while (!stack.empty() && height[current] > height[stack.peek()]) {
int h = height[stack.peek()]; //取出要出栈的元素
stack.pop(); //出栈
if (stack.empty()) { // 栈空就出去
break;
}
int distance = current - stack.peek() - 1; //两堵墙之前的距离。
int min = Math.min(height[stack.peek()], height[current]);
sum = sum + distance * (min - h);
}
stack.push(current); //当前指向的墙入栈
current++; //指针后移
}
return sum;
}
除了上述方法之外还有暴力法、动态规划、双指针的解法,这三种解法思想相同,都是求每一列的水,我们只需要关注当前列,以及左边最高的墙,右边最高的墙就够了。装水的多少,当然根据木桶效应,我们只需要看左边最高的墙和右边最高的墙中较矮的一个就够了。都可以查看上面那个链接进行学习
柱状图中最大的矩形
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为[2,1,5,6,2,3]
。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10
个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
这题就是比较标准的单调栈了,参考题解为
- https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/84-by-ikaruga/
- https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/bao-li-jie-fa-zhan-by-liweiwei1419/
两者代码其实差不多,但是一个讲解了一下单调栈+哨兵的技巧
没有加哨兵的版本
import java.util.ArrayDeque;
import java.util.Deque;
public class Solution {
public int largestRectangleArea(int[] heights) {
int len = heights.length;
if (len == 0) {
return 0;
}
if (len == 1) {
return heights[0];
}
int res = 0;
Deque<Integer> stack = new ArrayDeque<>(len);
for (int i = 0; i < len; i++) {
// 这个 while 很关键,因为有可能不止一个柱形的最大宽度可以被计算出来
while (!stack.isEmpty() && heights[i] < heights[stack.peekLast()]) {
int curHeight = heights[stack.pollLast()];
while (!stack.isEmpty() && heights[stack.peekLast()] == curHeight) {
stack.pollLast();
}
int curWidth;
if (stack.isEmpty()) {
curWidth = i;
} else {
curWidth = i - stack.peekLast() - 1;
}
// System.out.println("curIndex = " + curIndex + " " + curHeight * curWidth);
res = Math.max(res, curHeight * curWidth);
}
stack.addLast(i);
}
//未加哨兵,所以最后怕栈里面不干净,还需要计算再计算
while (!stack.isEmpty()) {
int curHeight = heights[stack.pollLast()];
while (!stack.isEmpty() && heights[stack.peekLast()] == curHeight) {
stack.pollLast();
}
int curWidth;
if (stack.isEmpty()) {
curWidth = len;
} else {
curWidth = len - stack.peekLast() - 1;
}
res = Math.max(res, curHeight * curWidth);
}
return res;
}
}
加哨兵,发现代码简洁很多
import java.util.ArrayDeque;
import java.util.Deque;
public class Solution {
public int largestRectangleArea(int[] heights) {
int len = heights.length;
if (len == 0) {
return 0;
}
if (len == 1) {
return heights[0];
}
int res = 0;
//两边加0
int[] newHeights = new int[len + 2];
newHeights[0] = 0;
System.arraycopy(heights, 0, newHeights, 1, len);
newHeights[len + 1] = 0;
len += 2;
heights = newHeights;
Deque<Integer> stack = new ArrayDeque<>(len);
// 先放入哨兵,在循环里就不用做非空判断
stack.addLast(0);
for (int i = 1; i < len; i++) {
while (heights[i] < heights[stack.peekLast()]) {
int curHeight = heights[stack.pollLast()];
int curWidth = i - stack.peekLast() - 1;
res = Math.max(res, curHeight * curWidth);
}
stack.addLast(i);
}
return res;
}
public static void main(String[] args) {
// int[] heights = {2, 1, 5, 6, 2, 3};
int[] heights = {1, 1};
Solution solution = new Solution();
int res = solution.largestRectangleArea(heights);
System.out.println(res);
}
}