如果说给你一个直方图,每个直方条的宽度为一,问你其中的某些直方条连在一起组成的最大的矩形面积是多少?
其实这道题就是相当于给你一个int height数组,表示每个直方条的高度,让你求最大的矩形的面积。我们知道对于每个直方条i,我们按照贪心的思路,将左右两边所有的height[j]>=height[i]的直方条都连在一起的话,得到的肯定是包含直方条i的最大矩形面积。
于是问题现在变成了对于当前的直方条i,我们需要求l[i]和r[i]分别表示左右两边所能延伸到的最远的直方条编号,如果我们已经知道了l[]和r[],那么我们显然是可以在O(n)的时间内求的最大的矩形的面积的,
也就是max(height[i] * (r[i]-l[i]+1))。
那么我们如何在O(n)的时间内得到l[]和r[]呢?
我们只说明如何求的l[]即可,其中l[i] = k表示最左边的直方条k,height[k]>=height[i]。
如果height[] = {2, 1, 5, 6, 2, 3},
那么此时的l[] = {0, 0, 2, 3, 2, 5}。
我们发现如果height[j] >= height[i],那么在区间[height[j], j]内的所有元素都是大于等于height[i],于是使用这个思路我们跳跃求i对应的l[i]。
那么为什么这样做的时间复杂度就是O(n)呢?我们来看对于任意一个j,如果height[j]>=height[i],那么区间[l[j], j]将会被[l[i], i]覆盖,于是这样被覆盖后的j是永远不会被重复访问第二次的,为什么呢?因为我们从右到左的比较height[i]与height[i`]的关系是,如果height[i] >= height[i`],于是区间[l[j], j]的height值一定也大于等于height[i`],也就是说直接将[l[i`], i`]区间覆盖了区间[l[i], i],也就覆盖了区间[l[j], j],所以j不会被重复访问第二次。因此每个i只会被访问一次,加上遍历的一边,也就是复杂度近似O(2n)的。
于是总的复杂度就是O(n)。
class Solution { public: int largestRectangleArea(vector<int> &height) { int n = height.size(); int *r = new int[n]; int *l = new int[n]; for(int i=0; i<n; i++){ l[i] = i; int j = i-1; while(j>=0 && height[j]>=height[i]){ l[i] = l[j]; j = l[j] - 1; } } for(int i=n-1; i>=0; i--){ r[i] = i; int j = i+1; while(j<n && height[j]>=height[i]){ r[i] = r[j]; j = r[j] + 1; } } int ans = 0; for(int i=0; i<n; i++){ int temp = height[i]*(r[i]-l[i]+1); if(temp > ans) ans = temp; } delete[] l; delete[] r; return ans; } };
在下面这个非常经典的问题中也可以使用该方法进行优化。
求一个01矩阵中最大的1矩阵,如果我们直接暴力求解,时间复杂度是O(n^4)的;而如果稍加优化,首先预处理成每一行为一个直方图问题,在每一行的直方图问题上进行暴力枚举每个连续的区间,那么总的时间复杂度是O(n^3)的;而如果我们对每一行的直方图问题使用上面的思路来求解,于是总的复杂度就降到了O(n^2),这显然是一个非常好的优化。
Largest Rectangle in Histogram,布布扣,bubuko.com
Largest Rectangle in Histogram
原文:http://blog.csdn.net/geniusluzh/article/details/21185309