柱状图中最大的矩形
编号:0022
试题来源:leetcode
题目描述
给定 n n n个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为1。
求在该柱形图中,能够勾勒出来的矩形的最大面积。
解答算法
算法思路
我们在思考最大的矩形的时候,可以任意选取一个柱子i
,以其高度height[i]
作为基准,向左右两侧进行拓展,如果拓展到的柱子的高度大于等于height[i]
,那么其可以作为以当前高度作为高的举行的一部分,如果小于,那么就可以得到其边界。
这样可以得到左右边界,有了左右边界,同时又有高度,就有了面积。
要得到左右边界,最笨的方法是对其进行两次遍历,每一次都要从当前位置i
进行向左向右遍历,显然最坏的时间复杂度为
O
(
n
)
O(n)
O(n)。
我们可以使用一个栈stack
存放单调的heigth[i]
。考虑这样一个事实,如果有
h
e
i
g
h
t
[
i
]
≥
h
e
i
g
h
t
[
j
]
且
i
<
j
height[i]\geq height[j]且i<j
height[i]≥height[j]且i<j,那么显然从大于
i
i
i的一个位置进行寻找边界,无论如何也不能选取
i
i
i作为左边界,因为
j
j
j挡住了
i
i
i,因此可以设置这样一个单调栈stack
,存放height[i]
,以单调递增的顺序存放。
当我们处理位置i
的时候,假设栈中已经存在
a
0
,
a
1
,
…
,
a
k
,
a
k
+
1
,
…
,
a
m
a_0,a_1,\dots ,a_k,a_{k+1},\dots,a_m
a0,a1,…,ak,ak+1,…,am,那么假设
h
e
i
g
h
t
[
i
]
>
a
k
height[i] > a_k
height[i]>ak,那么显然
a
k
+
1
,
…
,
a
m
a_{k+1},\dots,a_m
ak+1,…,am都不能阻挡i
的向左拓展,因此
k
k
k就是其左边界。要找到左边界,我们只需要将栈顶大于等于height[i]
的值都pop出来,然后当前栈顶的坐标就是左边界。得到左边界后再将
h
e
i
g
t
h
[
i
]
heigth[i]
heigth[i]加入栈中,形成新的单调栈
代码实现
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size(); //数组长度
vector<int> left(n), right(n); //存储左右两个边界
stack<int> mono_stack; //定义一个栈
for (int i = 0; i < n; ++i) { //对原数组从左向右进行遍历,得到左边界
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) { //当栈顶值大于等于当前值,移除
mono_stack.pop();
}
left[i] = (mono_stack.empty() ? -1 : mono_stack.top()); //左边界的定义
mono_stack.push(i); //将当前值移入
}
mono_stack = stack<int>(); //对原数组进行从右向左进行遍历,得到右边界
for (int i = n - 1; i >= 0; --i) {
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
mono_stack.pop();
}
right[i] = (mono_stack.empty() ? n : mono_stack.top());
mono_stack.push(i);
}
int ans = 0; //对整个数组进行遍历,得到答案
for (int i = 0; i < n; ++i) {
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
复杂度分析
- 时间复杂度:整个过程中对数组只进行一次遍历,可以看出在单调栈的操作过程中,我们对每个元素只进行了一次入栈操作,同时也最多只有一次出栈操作,因此时间复杂度为 O ( n ) O(n) O(n)
- 空间复杂度:申请了左右边界数组,长度都为 n n n,因此空间复杂度为 O ( n ) O(n) O(n)