#算法系列#单调栈

问题引入

废话不多说,首先看一道leetcode原题,柱状图中最大的矩形。
题目描述:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
#算法系列#单调栈_第1张图片
输入: [2,1,5,6,2,3]
输出: 10

问题分析

直接思路就是枚举每一个矩形,以该矩形为高,然后向左向右延伸,就可以计算面积。那么问题来了:怎么向左向右延伸?—>左右边界如何确定?
其实很简单:
1、左边界:找到大于等于当前柱形高度的最左边元素的下标
2、右边界:找到大于等于当前柱形高度的最右边元素的下标
知道个关键后,代码就好处理了:

public int largestRectangleArea(int[] heights) {
	if(heights==null||heights.length==0)
		return 0;
	int maxArea=0;
	for(int i=0;i<heights.length;i++){
		// 找最左边大于等于 heights[i] 的下标
        int left = i;
        int curHeight = heights[i];
        while (left > 0 && heights[left - 1] >= curHeight) {
           left--;
        }
        //同理
        int right=i;
        while(right<heights.length-1&&heights[right+1]>=curHeight)
        	right++;
        int curArea=curHeight*(r-l+1);
        maxArea=Math.max(maxArea,curArea);
	}
	return maxArea;
}

当然这里的时间复杂度是 0 ( N 2 ) 0(N^2) 0(N2)

优化

上面的思路在对每一个矩形找他的左边界时,出现了信息冗余,比如我对第一个矩形找到了他的左边界,其实我这里就知道第一个矩形左边所有的矩形分布情况,对与第二个矩形找左边界时,我完全可以利用第一个矩形保留下来的先验信息,而不是像暴力解法一样直接丢弃重新寻找。问题来了:用什么来报错这个先验信息呢?–结合上面的思路,左边界是:大于等于当前柱形高度的最左边元素的下标,转换一下角度:那左边界的前一个元素一定是小于当前柱形,我们叫它伪边界吧,所以找到了这个伪边界就相当于找到了左边界!这个时候思路就是存储一些不断递增的元素的数据结构,那就是单调栈。
结合暴力思想和单调栈的性质,代码就不难实现:
1、右边界:如果遍历到一个矩形时,它的高度比栈顶矩形小,那么这个矩形就是栈中某些矩形的伪右边界
2、左边界:找到了右边界后,由于栈中都是单调递增的,所以伪左边界就是栈顶的下一个矩形。

具体的流程放在代码中解释

public int largestRectangleArea(int[] heights) {
	if(heights==null||heights.length==0)
		return 0;
	//官方已将不建议使用Stack建栈了,使用链表速度更快
	LinkedList<Integer>stack=new LinkedList<>();
	//放入一个哨兵节点
	stack.add(-1);
	int maxArea=0;
	for(int i=0;i<heights.length;i++){
		//当前矩形高度小于栈顶矩形高度时,计算以它为伪右边界,栈顶元素的下一个元素为伪左边界的面积,直到当前矩形高度大于栈顶元素高度为止
		while(stack.getLast()!=-1&&heights[stack.getLast()>heights[i])
			maxArea=Math.max(maxArea,heights[stack.pollLast()]*(i-stack.getLast()-1));
		//不断将单调递增的元素放入栈中
		stack.addLast(i);
	}
	while(stack.getLast()!=-1)
		maxArea=Math.max(maxArea,heights[stack.pollLast()]*(heights.length-stack.getLast()-1));
	return maxArea;
}

时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( N ) O(N) O(N)

其他

这里还有几道单调栈题型:

序号 题目 题解
1 42. 接雨水(困难) 暴力+双指针+单调栈
2 739. 每日温度(中等) 暴力解法 + 单调栈
3 496. 下一个更大元素 I(简单) 暴力解法+单调栈
4 581. 最短无序连续子数组 单调栈+双指针

你可能感兴趣的:(算法学习)