题目链接:柱状图中最大的矩形
题目描述:
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
题目分析:矩形的面积是由宽度和高度共同决定的,因此我们可以通过枚举这两个值来求最大值。假如就选择高度好了,对于每个H[i],我们从这个点开始向两边扩展,只要大于等于就能继续扩展,直到两边的边界值均小于H[i],此时的范围即i位置能达到的最大面积值
代码:
class Solution {
public int largestRectangleArea(int[] heights) {
int ans = 0;
int n = heights.length;
int l,r,leftMax = 0,rightMax = 0;
for(int i = 0;i < n;i++){
l = i;
//向左扩展
while(l>0&&heights[l-1]>=heights[i]) l--;
leftMax = l;
r = i;
//向右扩展
while(r<n-1&&heights[r+1]>=heights[i]) r++;
rightMax = r;
ans = Math.max(ans,(rightMax-leftMax+1)*heights[i]);
}
return ans;
}
}
时间复杂度为O(N^2)
优化:
单调栈是一种数据结构,维护的是递增/减的一组数据,一般情况下我们只需要在栈中存储数据的索引值即可。然后根据题目的需求选择递增还是递减。
对于每个i位置能达到的最大面积值,我们在向两边扩展的时候,比如向左边扩展,那么寻找的边界是左侧距离i最近且小于H[i]的位置,因此可以使用一个递增单调栈,h[i]比栈顶元素大的就进栈,直到遇到比栈顶元素小的h[i],此时我们就将栈顶元素出栈,直到栈顶元素小于H[i],此时栈顶元素一定是离i最近且小于H[i]的元素。简单来说就是,栈里面保存的是大于H[i]的,遇到H[i]了,此时我们就把那些大于H[i]的依次出栈,这样最后剩的肯定是大于H[i]的第一个元素,也就是我们的边界。右边同理,只不过寻找的边界是右侧距离i最近且小于H[i]的位置
需要特别处理的是h[0]与h[n-1],可以设置虚拟柱子,最左边为-1,最右边为n
注意栈维护的信息是索引值而不是元素值
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
Stack<Integer> st= new Stack<Integer>();
for (int i = 0; i < n; ++i) {
while (!st.isEmpty() && heights[st.peek()] >= heights[i]) {
st.pop();
}
left[i] = (st.isEmpty() ? -1 : st.peek());
st.push(i);
}
st.clear();
for (int i = n - 1; i >= 0; --i) {
while (!st.isEmpty() && heights[st.peek()] >= heights[i]) {
st.pop();
}
right[i] = (st.isEmpty() ? n : st.peek());
st.push(i);
}
int ans = 0;
for (int i = 0; i y< n; ++i) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
}
优化 :在求解left[i]的同时求解right[i] ,只需要在出栈的同时更新right即可。原因也很简单,当栈顶元素大于h[i]时,此时对于栈顶元素来说,h[i]是右侧第一个小于它的边界值。
class Solution {
public int largestRectangleArea(int[] heights) {
int ans = 0;
int n = heights.length;
Stack<Integer> st = new Stack<Integer>();
int[] left = new int[n];
int[] right = new int[n];
Arrays.fill(right, n);
for(int i = 0;i < n;i++){
while(!st.isEmpty()&&heights[st.peek()]>=heights[i]){
right[st.peek()] = i;
st.pop();
}
left[i] = st.isEmpty()? -1 : st.peek();
st.push(i);
}
for(int i = 0;i < n;i++){
ans = Math.max(ans,(right[i]-left[i]-1)*heights[i]);
}
return ans;
}
}
由于每个元素只会入栈出栈仅一次,因此时间复杂度为O(n)
本质利用栈的先进后出的思想,再结合单调的性质解决问题。