算法分析与设计,第15周博客
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.
For example,
Given heights = [2,1,5,6,2,3]
,
return 10
.
设对于数组里的每个数h[i],都配有两个标志,分别标明关于这第i个长方形可以延伸到的最左端与最右端(两端的不包含),记这两个数组分别为mostLeft和mostRight,那么关于第i个长方形能夠形成的最大面积即为:h[i] *( mostRight[i] - mostLeft[i] -1)。那么这个问题的答案即为:
for (int i = 0; i < n; ++i) {
area = max(area, (mostRight[i]-mostLeft[i]-1)*h[i]);
}
那么,这个问题就变成了如何找到每一个数的最左端和最右端。最简单的方法就是遍历一次,那么这样的时间复杂度为O(n*n),并不算是非常好的一个办法。因为我们对于每个数之间都是分散的,其中没有任何的关联。那么,能否在一次遍历中就把每个数的上下界都找出来呢?这就需要一些小小的帮助了。
使用一个栈,其中这个栈中的元素是这样分布的,栈顶的元素是最大的,并且每弹出当前栈顶后,下一个栈顶也是栈中最大的,也就是这个栈是有序的。对于每个h[i],具体的操作如下:
1.栈是空的,或者h[i] 大于当前的栈顶,那么把i压入栈中。
2.如果当前的栈顶大于h[i]。那么说明对于当前的top元素,它的最右端就是i,也就是mostRight[top] = i;而这个栈又是有序的,那么下一个栈顶元素就是当前top的最左端,即mostLeft[top] = s.top();
这样,一次遍历过后,基本每个数都确定了其上下界,当然,这时栈不一定是空的,因为最小的元素和最后一个元素都可能依然还在栈中,所以,我们需要把整个栈清空,此时,对于栈中的每个元素,都有:
mostRight[top] = n; mostLeft[top] = s.empty() ? -1 : s.top();
这样,就确定了 每个元素的上下界,那么这个问题也就达到了解决,整体的代码如下:
class Solution {
public:
int largestRectangleArea(vector<int>& h) {
int n = h.size();
if (n == 0)
return 0;
stack<int> s;
int area = 0;
vector<int> mostLeft(n, -1);
vector<int> mostRight(n, n);
for (int i = 0; i < n; ++i) {
if (s.empty() || h[i] >= h[s.top()]) {
s.push(i);
} else {
while (!s.empty() && h[i] < h[s.top()]) {
int top = s.top();
s.pop();
mostRight[top] = i;
mostLeft[top] = s.empty() ? -1 : s.top();
}
s.push(i);
}
}
while (!s.empty()) {
int top = s.top();
s.pop();
mostRight[top] = n;
mostLeft[top] = s.empty() ? -1 : s.top();
}
for (int i = 0; i < n; ++i) {
area = max(area, (mostRight[i]-mostLeft[i]-1)*h[i]);
}
return area;
}
};
那么,最后来看下这个算法的时间复杂度和空间复杂度,因为使用了3个大小为O(n)的容器,所以空间复杂度为O(n)。时间复杂度主要分为两部分一个部分是得到上下界后确定最大的面积,这个部分的时间复杂度为O(n);另外一个部分就是计算每个元素的上下界,关于这个部分,直接的观察代码可能不太好计算,但是我们可以从原理上来理解,一个有n个数,而且这n个数都会被压入栈中,而其中入栈和出栈的操作所花的时间都是O(1),计算上下界的操作也是O(1),那么,总体的时间复杂也就是O(1*n)。那么最终整个算法的时间复杂度是O(n)。