LeetCode解题 84:Largest Rectangle in Histogram
Problem 84: Largest Rectangle in Histogram [Hard]
Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3]
.
The largest rectangle is shown in the shaded area, which has area = 10
unit.
Example:
Input: [2,1,5,6,2,3]
Output: 10
来源:LeetCode
解题思路
1. 分治法
在一组不同高度的柱子中求取最大面积,可以通过将其中的最矮柱子作为分割点进行分治,公式为:
m
a
x
a
r
e
a
(
0
,
N
−
1
)
=
max
{
m
a
x
a
r
e
a
(
0
,
i
n
d
e
x
∗
−
1
)
,
N
∗
h
e
i
g
h
t
s
[
i
n
d
e
x
∗
]
,
m
a
x
a
r
e
a
(
i
n
d
e
x
∗
+
1
,
N
−
1
)
}
max_{area}(0, N-1) = \max\{max_{area}(0, index^*-1), N * heights[index^*], max_{area}(index^*+1, N-1) \}
maxarea(0,N−1)=max{maxarea(0,index∗−1),N∗heights[index∗],maxarea(index∗+1,N−1)}
其中
i
n
d
e
x
∗
index^*
index∗为最矮柱子的下标,
m
a
x
a
r
e
a
max_{area}
maxarea是求取最大面积的函数。
时间复杂度为 O ( n log n ) O(n \log n) O(nlogn)。
2. 栈
栈的思想是用空间换取时间,遍历过程中,保证栈内的下标对应的柱子高度值是递增的,这样对于每个不同高度的柱子,都可以在pop()时算出该高度向左向右扩展形成的最大面积。
一个恒定的要素为:(stack[pop-1] ~ stack[pop]) 和 (stack[pop+1] ~ i)下标范围内的柱子高度都大于等于下标为stack[pop]的柱子高度,即:
∀
i
n
d
e
x
∈
(
s
t
[
p
o
p
−
1
]
,
s
t
[
p
o
p
]
)
∪
(
s
t
[
p
o
p
]
,
i
)
,
h
e
i
g
h
t
s
[
i
n
d
e
x
]
≥
h
e
i
g
h
t
s
[
s
t
[
p
o
p
]
]
.
\forall index \in (st[pop-1], st[pop]) \cup (st[pop], i), \\ heights[index] \geq heights[st[pop]].
∀index∈(st[pop−1],st[pop])∪(st[pop],i),heights[index]≥heights[st[pop]].
因此,对于下标为
s
t
[
p
o
p
]
st[pop]
st[pop]的柱子,以其高度可扩展的最大面积为
h
e
i
g
h
t
s
[
s
t
[
p
o
p
]
]
∗
(
i
−
s
t
[
p
o
p
−
1
]
−
1
)
heights[st[pop]] * (i - st[pop-1] - 1)
heights[st[pop]]∗(i−st[pop−1]−1)。
其中
s
t
[
p
o
p
]
st[pop]
st[pop]表示出栈的下标,
s
t
[
p
o
p
−
1
]
st[pop-1]
st[pop−1]表示出栈元素的前一个下标,
i
i
i表示当前遍历到的下标。
具体过程:
- 首先需要使用哨兵技巧,在栈中压入值-1,防止之后计算st[pop-1]时会越界。
- 然后遍历数组,不断压入高度递增的柱子下标
i
,直到heights[i] < heights[i-1]时停止
[1],此时对栈中原有的下标依次进行pop(),计算面积:area = heights[st[pop]] * (i - st[pop-1] -1),更新最大值:max_area = max{max_area, area},直到heights[st[pop]] < heights[i]时停止
[2],将当前下标i
压入栈。
注释[1]:这一步中的heights[i] < heights[i-1]时停止压栈,保证了(st[pop], i)下标范围内的柱子都大于等于st[pop]的高度。
注释[2]:heights[st[pop]] < heights[i]时停止pop(),保证了在i
压入栈后,(st[pop*-1], st[pop*])下标范围内的柱子都大于等于st[pop*]的高度。这里的st[pop*]就是刚压入栈的下标i
。 - 最后当遍历至数组尾部时,将栈中的下标都以此pop()并计算area。
整个算法入栈和出栈一共遍历了两次数组,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。
Solution (Java)
class Solution {
public int largestRectangleArea(int[] heights) {
int N = heights.length;
if(N == 0) return 0;
Stack<Integer> sk = new Stack<Integer>();
sk.push(-1);
sk.push(0);
int max = -1;
int area;
for(int i = 1; i < N; i++){
while(i < N && heights[i] >= heights[sk.peek()]){
sk.push(i);
i++;
}
if(i == N) break;
while(sk.peek() > -1 && heights[i] <= heights[sk.peek()]){
area = heights[sk.pop()] * (i - sk.peek() - 1);
max = Math.max(max, area);
}
sk.push(i);
}
while(sk.peek() > -1){
area = heights[sk.pop()] * (N - sk.peek() - 1);
max = Math.max(max, area);
}
return max;
}
}
修改过程
- for循环中间跳出while时需要检查一下是不是到了数组尾部,是的话要break出来对栈内元素依次pop()。