单调栈
1. 单调栈的结构
背景:给定一个数组,求数组中每一个元素的左边距离其最近的比它小的值,和右边距离其最近的比它小的值。
数组:
arr:{3,5,4,2,6}
arr[i] left right 3 null 2 5 3 4 4 3 2 2 null null 6 2 null
思路:设计一个栈,依次存入的元素大于等于栈顶元素,保持栈中元素从下往上逐渐增大。
- 如果要存入的元素
num
大于栈顶,则直接入栈。 - 如果要存入的元素
num
等于栈顶,则一起存入栈顶(栈顶中的每一个元素可以用链表实现)。 - 如果要存入的元素
num
小于栈顶,则弹出栈顶元素peek
,则num
为peek
的右边距离其最近且小于它的元素。当弹出peek
后,此时的栈顶元素peekCur
为peek
的左边距离其最近且小于它的元素。 - 当遍历完数组,栈不为空的话,依次弹出栈顶元素
peek
,此时该peek
的右边距离其最近且小于它的元素为null,左边距离其最近且小于它的元素为下一个栈顶元素。
2. 最大拼接的面积
题目:有如下矩形图,求该矩形图中能拼接成矩形的最大面积。
思路:用单调栈判断每个矩形左右两边,距离其最近,且比其矮的矩形。
题解:
- 黄色矩形高为5,由于右边棕色矩形比黄色矩形高,所以黄色矩形可以和棕色矩形拼接成高为5,宽为2的矩形。
- 由于棕色矩形左右两边的矩形都比它低,所以它能拼接成的矩形是他自己。高为6,宽为1。
- 橙色矩形最低,能拼接所有矩形,最终形成一个高为3,宽为5的矩形。
- 蓝色矩形也不能和其他矩形拼接,高为7,宽为1。
- 绿色矩形能和蓝色矩形拼接,形成高为4,宽为2的矩形。
3. 求最大子矩阵的大小
题目:给定一个整型矩阵map, 其中的值只有0 和 1 两种, 求其中全是1 的所有矩形区域中, 最大的矩形区域为1的数量。
例:
1 0 1 1
1 1 1 1
1 1 1 0其中,最大的矩形区域有6个1,所以返回6 。
思路:把矩阵的每一行看成矩形图。
- 如第一行
[1,0,1,1]
,把它看成矩形图,由四个矩形组成,高分别为1,0,1,1,宽都为1。求矩形最大拼接面积。 - 第二行
1,1,1,1
和第一行[1,0,1,1]
组合起来,组成一个矩形图,四个矩形高分别2,1,2,2,宽都为1。求矩形最大拼接面积。 - 最后一行把整个矩阵看成一个矩形图,注意,四个矩形高分别为3,2,3,0。求矩形最大拼接面积。
- 得到的矩形最大拼接面积的最大值即为结果。
代码:
import java.util.Stack;
class Solution_MaxArea{
public static int maxAreaMatrix(int[][] arr){
//判断矩阵是否为空
if(arr == null || (arr.length == 1 && arr[0].length == 0) || arr.length == 0){
return 0;
}
int result = Integer.MIN_VALUE;
int[] rectangle = new int[arr[0].length];
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr[i].length; j++){
rectangle[j] = arr[i][j] == 0 ? 0 : rectangle[j] + arr[i][j];
}
result = Math.max(maxAreaRectangle(rectangle), result);
}
return result;
}
public static int maxAreaRectangle(int[] arr){
if(arr == null || arr.length == 0){
return 0;
}
int len = arr.length;
Stack<Integer> stack = new Stack<>();
int res = Integer.MIN_VALUE;
//哪个元素先弹出,先计算以其代表的矩形的高为最大矩形的高,在遍历到的所有矩形中能合并成的最大矩形 的面积
for(int i = 0; i < len; i++){
//注意,是大于等于号。
while(!stack.isEmpty() && arr[stack.peek()] >= arr[i]){
int height = arr[stack.pop()];
int left = stack.isEmpty() ? -1 : stack.peek();
int right = i;
int area = height * (right - left - 1);
res = Math.max(area, res);
}
stack.push(i);
}
while(!stack.isEmpty()){
int height = arr[stack.pop()];
int right = len;
int left = stack.isEmpty() ? -1 : stack.peek();
int area = height * (right - left - 1);
res = Math.max(area, res);
}
return res;
}
public static void main(String[] args) {
int[][] arr = {{1,0,1,1},{1,1,1,1},{1,1,1,0}};
System.out.println(maxAreaMatrix(arr));
}
}
注意: maxAreaRectangle
求最大矩形面积的过程比较特别,对于没有遍历到的矩形,暂时不将其考虑进最大拼接矩形的范围中。比如说矩形2,2,4,6,2
,当遍历到i=1时,栈顶元素为1,由于栈顶元素所代表的值为2,与arr[2]相等,所以需要弹出。当前只遍历过一个矩形,所以能形成的最大矩形也就是栈顶元素所代表的那个矩形。