前言
笔试面试中,单调栈用的也特别多,属于数据结构类的,在这里先总结下,免得以后忘了。这里只讲应用,不太涉及原理。
题目
1 矩形最大面积
首先来个经典例题:
求矩形的最大面积。((leetcode 85 maximal_Recetangle leetcode 84 largest_rectangle_in_histogram))
这个题目就是典型的单调栈。
运用单点栈的时候,首先想到的,应该是,建立一个单调递增还是单调递减的单调栈。这里维护一个单调递增的单调栈。每次遇到一个小于当前栈顶的数时(假设这个数为tn),把当前栈中所有比tn大的数,全部弹出栈。弹出栈的过程中,不要忘了,在弹出的过程中,不要忘了做相应的操作(如增加矩形的宽)。
以这组数据为例:
3 5 10 12 7 8 20
刚开始面积s = 0,首先把3入栈,然后5大于3,5入栈,10大于5,10入栈,12大于10,12入栈,7小于12,12出栈,同时s 更新为12;10比7大,10出栈,更新s为102 = 20。然后7入栈,这个时候,7的宽为3(10,12,7,三个数全部压成了7)。8入栈,20入栈。所有数遍历完了之后,栈中内容为3(1),5(1),7(3),8(1),20(1),s=20。20出栈,s=20,8出栈,s=max(s,82)=20,6出栈,s=max(s,6*5) = 30。。。这样操作下去。
贴出这个题目AC的代码:
class R {
public int x;
public int y;
public R(int x, int y) {
this.x = x;
this.y = y;
}
}
public int largestRectangleArea(int[] height) {
if(height.length==0)
return 0;
Stack<R> stack = new Stack<>();
//维护一个单调递zeng的单调栈
int pos = 0;
int max = 0;
stack.add(new R(height[pos++], 1));
while (pos < height.length) {
\\ R tr = stack.peek();
int len = 1;
while (!stack.isEmpty() && (stack.peek().x > height[pos])) {
len += stack.peek().y;
max = Math.max((len - 1) * stack.peek().x, max);
stack.pop();
}
stack.push(new R(height[pos++], len));
}
int len = 0;
while (!stack.isEmpty()) {
len += stack.peek().y;
max = Math.max(max, len * stack.pop().x);
}
\\ System.out.print(max);
return max;
}
这是单调栈的一个典型应用,还有一个变形就是 给你一连串数字,要你找出一连串连续的数字,求出该区间段中所有数的和*该区间中最小值,也是单调栈的典型应用。
单调栈的时间复杂度,几乎可以认为为线性。
单点栈还有一类题目,经常出现,那就是 区间和的最小值
区间和的最小值
下面给一个参考链接,已经说的很详细了。
求数组中区间中最小数*区间所有数和的最大值
一维单调栈就说这么多吧,下面说说二维单调栈。
滑动窗口的最大值
滑动窗口的最大值,可以说是单调栈的经典运用了。
下面给出单调队列解题详解
额外提一句,这里有个很奇葩的想法,就是等分成n/k份,每份k的长度,left[i]代表到i的最大值,right[i]代表到i的最大值,详细解法可以看详细通俗的思路分析,多解法解法四
2 一维单调栈进阶——二维单调栈
先来一个二维单调栈的典型题目。
leetcode最大矩形面积
这里是二维单调栈的典型应用,遍历数组,每一层当做一个一维的单调栈去处理。关键在于得到单调栈的矩形,这个矩形由上面的矩形传递下来。如果当前a[i]为0,则这个矩形高度为0,如果a[i]为1,则矩形面积为a[i-1]+1。
AC代码如下:
class R {
int x;
int y;
public R(int x, int y) {
this.x = x;
this.y = y;
}
}
int dp[][];
public int maximalRectangle(char[][] matrix) {
if (matrix.length == 0)
return 0;
int len1 = matrix.length;
int len2 = matrix[0].length;
dp = new int[len1][len2];
for (int i = 0; i < len1; i++) {
for (int j = 0; j < len2; j++) {
dp[i][j] = 0;
}
}
for (int i = 0; i < len2; i++) {
dp[0][i] = matrix[0][i] == '1' ? 1 : 0;
}
Stack<R> stack = new Stack<>();
int a[] = new int[len2];
a[0] = matrix[0][0] == '1' ? 1 : 0;
int max = a[0];
for (int i = 1; i < len2; i++) {
if (matrix[0][i] == '1') {
a[i] = a[i - 1] + 1;
max = Math.max(max, a[i]);
} else {
a[i] = 0;
}
}
for (int i = 1; i < len1; i++) {
for (int j = 0; j < len2; j++) {
if (matrix[i][j] == '1') {
dp[i][j] = dp[i - 1][j] + 1;
}
int len = 1;
while (!stack.isEmpty() && (stack.peek().x > dp[i][j])) {
len = len + stack.peek().y;
max = Math.max(max, (len - 1) * stack.peek().x);
stack.pop();
}
stack.push(new R(dp[i][j], len));
}
int slen = 0;
while (!stack.isEmpty()) {
// System.out.print(stack.peek().x + " " + stack.peek().y + " ");
slen += stack.peek().y;
max = Math.max(max, stack.peek().x * slen);
stack.pop();
}
// System.out.println();
}
return max;
}
这时复杂度就是在一维单调栈上面加一维。
这个题目还有一个相关变形,就是二维矩形的最大和。不过这个就完全不是单调栈的揭发了,二十动态规划,后面有时间再总结。