柱状图中最大的矩形
- leetcode84. 柱状图中最大的矩形
- 题目描述
- 单调栈加数组优化栈结构解题
- 代码演示
- 用数组来优化栈结构,时间会更快
- 单调栈专题
leetcode84. 柱状图中最大的矩形
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/largest-rectangle-in-histogram
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例1:
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
示例2:
输入: heights = [2,4]
输出: 4
提示:
1 <= heights.length <=105
0 <= heights[i] <= 104
单调栈加数组优化栈结构解题
思路大致如下:
去遍历这个数组,每到一个数字时,我们就找出能以当前数字为最小值的子数组的范围,知道范围后,我们就可以求出其最大矩形的大小.遍历所有位置后,就可以得出最大值了.
具体流程:
首先我们枚举某一根柱子 ii 作为高 h=heights[i];
随后我们需要进行向左右两边扩展,使得扩展到的柱子的高度均不小于 h。换句话说,我们需要找到左右两侧最近的高度小于 h 的柱子,这样这两根柱子之间(不包括其本身)的所有柱子高度均不小于 h,并且就是 i 能够扩展到的最远范围。
我们用一个具体的例子 [6,7,5,2,4,5,9,3]来理解单调栈。我们需要求出每一根柱子的左侧且最近的小于其高度的柱子。初始时的栈为空。
我们枚举 6,因为栈为空,所以 6 左侧的柱子是「哨兵」,位置为 -1。随后我们将 6 入栈。
栈:[6(0)]。(这里括号内的数字表示柱子在原数组中的位置)
我们枚举 7,由于 6<7,因此不会移除栈顶元素,所以 7 左侧的柱子是 6,位置为 0。随后我们将 7 入栈。
栈:[6(0), 7(1)]
我们枚举 5,由于 7≥5,因此移除栈顶元素 7。同样地,6≥5,再移除栈顶元素 6。此时栈为空,所以 5 左侧的柱子是「哨兵」,位置为 −1。随后我们将 5 入栈。
栈:[5(2)]
接下来的枚举过程也大同小异。我们枚举 2,移除栈顶元素 5,得到 2 左侧的柱子是「哨兵」,位置为 −1。将 2 入栈。
栈:[2(3)]
我们枚举 4,5 和 9,都不会移除任何栈顶元素,得到它们左侧的柱子分别是 2,4和 5,位置分别为 3,4 和 5。将它们入栈。
栈:[2(3), 4(4), 5(5), 9(6)]
我们枚举 3,依次移除栈顶元素 9,5 和 4,得到 3 左侧的柱子是 2,位置为 3。将 3 入栈。
栈:[2(3), 3(7)]
这样以来,我们得到它们左侧的柱子编号分别为[−1,0,−1,−1,3,4,5,3]。用相同的方法,我们从右向左进行遍历,也可以得到它们右侧的柱子编号分别为 [2,2,3,8,7,7,7,8],这里我们将位置 8 看作「哨兵」。
在得到了左右两侧的柱子之后,我们就可以计算出每根柱子对应的左右边界,并求出答案了。
分析
单调栈的时间复杂度是多少?直接计算十分困难,但是我们可以发现:
每一个位置只会入栈一次(在枚举到它时),并且最多出栈一次。
因此当我们从左向右/总右向左遍历数组时,对栈的操作的次数就为 O(N)。所以单调栈的总时间复杂度为 O(N)。
代码演示
/**
* 单调栈
* @param height
* @return
*/
public static int largestRectangleArea1(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
int curArea = (height.length - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
用数组来优化栈结构,时间会更快
/**
* 单调栈, 数组来优化栈
* @param height
* @return
*/
public static int largestRectangleArea2(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int N = height.length;
//用数组来优化栈
int[] stack = new int[N];
//记录数组的有边界的位置
int si = -1;
//记录最大面积
int maxArea = 0;
for (int i = 0; i < height.length; i++) {
while (si != -1 && height[i] <= height[stack[si]]) {
int j = stack[si--];
int k = si == -1 ? -1 : stack[si];
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
//加入到队列中
stack[++si] = i;
}
while (si != -1) {
int j = stack[si--];
int k = si == -1 ? -1 : stack[si];
int curArea = (height.length - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
单调栈专题
单调栈的实现-单调递减栈和单调递增栈
leetcode1856. 子数组最小乘积的最大值