【LeetCode】 Largest Rectangle in Histogram O(n) 解法详析
2015-12-09 13:58
267 查看
转载自:/article/6171332.html
给定n个非负整数,代表柱形图的每个柱形条的高度,假设每个柱形条的宽度为1,求该柱形图中最大矩形的面积。
如上图所示,给定的高度序列为 height=[2,1,5,6,2,3]。
所以该柱形图的最大矩形就是上图阴影部分面积,maxArea=5*2=10。
遍历所有立柱,对当前立柱i,以其高度左右扩展,看看以当前立柱i的高度最多能包含进多大的矩形面积,最后选出最大的面积即可。代码如下:
这个思路虽然可行,但测试数据过大就会超时。优化后的思路如下:
height的内容是 [5,6,7,8,3],特点是除了最后一个,前面全部保持递增,且最后一个立柱的高度小于前面所有立柱高度。
对于这种特点的柱状图,如果使用上面所说的“挨个使用每一个柱状图的高度作为矩形的高度,求面积”的方法,还需要用嵌套循环吗?
我们知道除了最后一个,从第一个到倒数第二个立柱的高度都在升高,那么如果挨个使用每一个柱的高度作为矩形的高度,那么依次能得到的矩形的宽度就可以直接算出来:使用5作为高度可以使用前四个立柱组成 4*5的矩形,高度6可以组成3*6的矩形… 因此只需要遍历一次,选出最大面积即可。
我们将这种特点的柱状图称为“波峰图”。
(2) 定义了一个stack,然后遍历时如果height[i] 大于stack.top(),进栈。反之,出栈直到栈顶元素小于height[i]。
由于出栈的这些元素高度都是递增的,我们可以求出这些立柱中所围成的最大矩形。更妙的是,由于这些被弹出的立柱处于“波峰”之上(比如弹出i 到 i+k,那么所有这些立柱的高度都高于 i-1和 i+k+1的高度),因此,如果我们使用之前所提的“左右延伸找立柱”的思路解,以这些立柱的高度作为整个矩形的高度时,左右延伸出的矩形所包含的立柱不会超出这段“波峰”,因为波峰外的立柱高度都比他们低。“波峰图”其实就是求解最大矩形的“孤岛”,它不会干扰到外部。
(3) 由于比height[i]大的元素都出完了,height[i]又比栈顶元素大了,因此再次进栈。如此往复,直到遍历到最后那个高度为0的柱,触发最后的弹出以及最后一次面积的计算,此后stack为空。
(4) 返回面积最大值。
栈中存的不是高度,而是height的索引,这样做的好处是不会影响宽度的计算,索引值相减 = 宽度。
虽然是二重循环,但时间复杂度实际 2N,故为O(N)。代码如下:
(1) stack里存的是index,计算面积时的宽度使用 index的差值,所以虽然stack 弹出了立柱,但是不影响宽度的计算,依然可以计算面积。
(2) 这种解法本质上是查看以每一个立柱为矩形高度,求出最大面积,但是它通过入栈出栈,把整个height变成一组组“波峰图”来解,这种高度布局下,最大面积的计算是O(n)的,然后将所有波峰图的最大面积取最大值。最后做到了以O(n)的时间复杂度覆盖了所有的立柱。
题目大意:
原题链接:https://leetcode.com/problems/largest-rectangle-in-histogram/给定n个非负整数,代表柱形图的每个柱形条的高度,假设每个柱形条的宽度为1,求该柱形图中最大矩形的面积。
如上图所示,给定的高度序列为 height=[2,1,5,6,2,3]。
所以该柱形图的最大矩形就是上图阴影部分面积,maxArea=5*2=10。
解题思路:
思路一:
时间复杂度O(n²)遍历所有立柱,对当前立柱i,以其高度左右扩展,看看以当前立柱i的高度最多能包含进多大的矩形面积,最后选出最大的面积即可。代码如下:
class Solution { public: int largestRectangleArea(vector<int> &height) { if(height.size() == 0) return 0; int max = 0; for(int i = 0; i < height.size(); ++i){ int mid = i; int area = 0; for(;mid >= 0 && height[mid] >= height[i]; area += height[i], --mid); for(mid = i+1 ;mid < height.size() && height[mid] >= height[i]; area += height[i], ++mid); if(max < area) max = area; } return max; } };
这个思路虽然可行,但测试数据过大就会超时。优化后的思路如下:
思路二:
时间复杂度O(n)height的内容是 [5,6,7,8,3],特点是除了最后一个,前面全部保持递增,且最后一个立柱的高度小于前面所有立柱高度。
对于这种特点的柱状图,如果使用上面所说的“挨个使用每一个柱状图的高度作为矩形的高度,求面积”的方法,还需要用嵌套循环吗?
我们知道除了最后一个,从第一个到倒数第二个立柱的高度都在升高,那么如果挨个使用每一个柱的高度作为矩形的高度,那么依次能得到的矩形的宽度就可以直接算出来:使用5作为高度可以使用前四个立柱组成 4*5的矩形,高度6可以组成3*6的矩形… 因此只需要遍历一次,选出最大面积即可。
我们将这种特点的柱状图称为“波峰图”。
下面该解法的步骤:
(1) 在height尾部添加一个0,也就是一个高度为0的立柱。作用是在最后也能凑成上面提的那种“波峰图”。(2) 定义了一个stack,然后遍历时如果height[i] 大于stack.top(),进栈。反之,出栈直到栈顶元素小于height[i]。
由于出栈的这些元素高度都是递增的,我们可以求出这些立柱中所围成的最大矩形。更妙的是,由于这些被弹出的立柱处于“波峰”之上(比如弹出i 到 i+k,那么所有这些立柱的高度都高于 i-1和 i+k+1的高度),因此,如果我们使用之前所提的“左右延伸找立柱”的思路解,以这些立柱的高度作为整个矩形的高度时,左右延伸出的矩形所包含的立柱不会超出这段“波峰”,因为波峰外的立柱高度都比他们低。“波峰图”其实就是求解最大矩形的“孤岛”,它不会干扰到外部。
(3) 由于比height[i]大的元素都出完了,height[i]又比栈顶元素大了,因此再次进栈。如此往复,直到遍历到最后那个高度为0的柱,触发最后的弹出以及最后一次面积的计算,此后stack为空。
(4) 返回面积最大值。
栈中存的不是高度,而是height的索引,这样做的好处是不会影响宽度的计算,索引值相减 = 宽度。
虽然是二重循环,但时间复杂度实际 2N,故为O(N)。代码如下:
class Solution { public: int largestRectangleArea(vector<int>& height) { int maxArea = 0; if (height.size() == 0) return 0; height.push_back(0); stack<int> s; int i, j; for (i = 0; i < height.size(); ++i){ if (!s.empty() && height[i] < height[s.top()]){ while (height[i] <= height[s.top()]){ int tmp = s.top(); s.pop(); int area = (s.empty() ? i : i - s.top() - 1) * height[tmp]; maxArea = (maxArea>area) ? maxArea : area; if (s.empty())break; } } s.push(i); }//for return maxArea; } };
总结
思路二的亮点就在于:(1) stack里存的是index,计算面积时的宽度使用 index的差值,所以虽然stack 弹出了立柱,但是不影响宽度的计算,依然可以计算面积。
(2) 这种解法本质上是查看以每一个立柱为矩形高度,求出最大面积,但是它通过入栈出栈,把整个height变成一组组“波峰图”来解,这种高度布局下,最大面积的计算是O(n)的,然后将所有波峰图的最大面积取最大值。最后做到了以O(n)的时间复杂度覆盖了所有的立柱。
相关文章推荐
- C# 关于方法中的参数(可选参数和命名参数)
- CSS中的数量查询
- 教学产品设计
- Visual Studio控制台程序输出窗口一闪而过的解决方法
- 时间如流水般逝去—兄弟连IT教育
- 编写iOS HTTP网络请求库(总)
- hdu 1060(取前n位)
- JAVA获取当前系统时间System.currentTimeMillis()
- 10014---Nginx 概述
- Swift第二天学习
- FAT32文件系统
- 跟我从零基础学习Unity3D开发--资源打包篇(AssetBundle)
- 打造H5动感影集的爱恨情仇–动画性能篇
- Java中的增补字符
- java多线程之模拟死锁
- 欢迎使用CSDN-markdown编辑器
- 补码
- ubuntu 14.04上源码编译安装php7
- Java代码中的字符编码转换
- 异步图片下载问题随笔