LeetCode-Largest Rectangle in Histogram O(n) 详析-ZZ
http://www.cnblogs.com/felixfang/p/3676193.html
Largest Rectangle in Histogram
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 height = [2,1,5,6,2,3]
,
return 10
.
思路:如果时间复杂度要求是O(n2)的话,解法比较多也比较好理解。比如可以遍历,对于当前 i 位置上的立柱,计算出以这个i 立柱结尾的最大矩形,然后求出总的最大矩形。
计算以i 立柱结尾的最大矩形又需要一次遍历,因此时间复杂度是 O(n2)。
或者可以用另一种方法:最大矩形的高度毫无疑问必然和某一个立柱的高度相等,或者说,最大矩形必然包含了某一个立柱的全部。
因此,可以遍历所有立柱,对当前立柱 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的矩形... 因此只需要遍历一次,选出最大面积即可。
对于这种类型的柱状图,最大矩形面积的时间复杂度是O(n)。
我们将这种特点的柱状图称为“波峰图”。
下面介绍新的解法的步骤:
(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) {
if(height.size() == 0) return 0;
stack<int> st;
int MAX = 0;
height.push_back(0);
int leftarea = 0, rightarea = 0;
for(int i = 0; i < height.size(); ++i){
while(!st.empty() && height[st.top()] > height[i]){
int tmp = st.top();
st.pop();
leftarea = (st.empty() ? tmp + 1 : tmp - st.top()) * height[tmp]; //以tmp为高度,tmp所在柱以及向左延伸出来的矩形面积
rightarea = (i - tmp - 1) * height[tmp]; //以tmp为高度,向右边延伸出来的矩形面积
if((leftarea + rightarea) > MAX) MAX = (leftarea + rightarea);
}
st.push(i);
}
return MAX;
}
};
100 ms AC
另一版稍简介的代码 ,引自 水中的鱼-[LeetCode] Largest Rectangle in Histogram 解题报告
1: int largestRectangleArea(vector<int> &h) {
2: stack<int> S;
3: h.push_back(0);
4: int sum = 0;
5: for (int i = 0; i < h.size(); i++) {
6: if (S.empty() || h[i] > h[S.top()]) S.push(i);
7: else {
8: int tmp = S.top();
9: S.pop();
10: sum = max(sum, h[tmp]*(S.empty()? i : i-S.top()-1));
11: i--;
12: }
13: }
14: return sum;
15: }
108 ms AC
此解法最大亮点就在于
(1) stack里存的是index,计算面积时的宽度使用 index的差值,所以虽然stack 弹出了立柱,但是不影响宽度的计算,依然可以计算面积。
(2) 这种解法本质上是查看以每一个立柱为矩形高度,求出最大面积,但是它通过入栈出栈,把整个height变成一组组“波峰图”来解,这种高度布局下,最大面积的计算是O(n)的,然后将所有波峰图的最大面积取最大值。最后做到了以O(n)的时间复杂度覆盖了所有的立柱。
多么精彩的解法!
接下来还有道Maximal Rectangle 的题,这道题的实用价值很大:算01 矩阵中包含最多1 的矩形。
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.
有了上一题的基础,这道题就可等效为上一题,对于矩阵每一行,我们将其看作直方图,立柱的高度就是行中元素往上数包含的连续1的个数。
因此每一行都可以利用上一题方法计算最大矩形,最后求出各行结果的最大值就好了。时间复杂度 O(n2)
class Solution {
public:
int maximalRectangle(vector<vector<char> > &matrix) {
if(matrix.size() == 0 || matrix[0].size() == 0) return 0;
int H = matrix.size(), W = matrix[0].size();
int height[W+1];
int i, j , MAX = 0, leftarea = 0, rightarea = 0;
stack<int> st;
for(i = 0; i <= W; height[i] = 0, ++i);
for(i = 0; i < H; ++i){
while(!st.empty()) st.pop();
for(j = 0; j < W; ++j){
if(matrix[i][j] == '1') height[j]++;
else height[j] = 0;
}
for(int j = 0; j <= W; ++j){
while(!st.empty() && height[st.top()] > height[j]){
int tmp = st.top();
st.pop();
leftarea = (st.empty() ? tmp + 1 : tmp - st.top()) * height[tmp];
rightarea = (j - tmp - 1) * height[tmp];
if((leftarea + rightarea) > MAX) MAX = (leftarea + rightarea);
}
st.push(j);
}
}
return MAX;
}
};
88ms AC
总结:
第一题中,能完成那样精彩的解法,stack 的灵活使用功不可没,这样使用stack可能一上来不容易想到。
但是如果我们遇到这道题的时候,一开始应该想想特例,比如递增序列下的最大矩形面积,然后发散开来,想想一般情况和这种递增情况的关系,也许就能有突破。使用类似的"从特例到一般"的发散方式还有Candy (分糖果)的第二种解法。
题外话:最近在看《一万小时理论》,感觉到所谓天才,不过是不停的总结,在练习和总结中,慢慢地能够熟练运用正确的思考方法和找到正确的思路,从而可以在较短时间内给出解的人。一起努力加厚自己的髓鞘质吧 :)
LeetCode-Largest Rectangle in Histogram O(n) 详析-ZZ的更多相关文章
- [LeetCode] Largest Rectangle in Histogram O(n) 解法详析, Maximal Rectangle
Largest Rectangle in Histogram Given n non-negative integers representing the histogram's bar height ...
- leetcode Largest Rectangle in Histogram 单调栈
作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4052343.html 题目链接 leetcode Largest Rectangle in ...
- LeetCode: Largest Rectangle in Histogram 解题报告
Largest Rectangle in Histogram Given n non-negative integers representing the histogram's bar height ...
- [LeetCode] Largest Rectangle in Histogram 直方图中最大的矩形
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
- LeetCode: Largest Rectangle in Histogram(直方图最大面积)
http://blog.csdn.net/abcbc/article/details/8943485 具体的题目描述为: Given n non-negative integers represent ...
- [leetcode]Largest Rectangle in Histogram @ Python
原题地址:https://oj.leetcode.com/problems/largest-rectangle-in-histogram/ 题意: Given n non-negative integ ...
- [LeetCode] Largest Rectangle in Histogram
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
- leetcode -- Largest Rectangle in Histogram TODO O(N)
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
- [LeetCode] Largest Rectangle in Histogram 解题思路
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
随机推荐
- es第四篇:Query DSL
Query and filter context Match All Query 最简单的search请求,匹配所有文档,文档的_score值都是1,示例: get twitter/_search{ ...
- 计算机网络c++实现截断二进制指数退避算法
#include<iostream> #include<vector> #include <stdio.h> #include<stdlib.h> // ...
- Clarke Award for Imagination in Service to Society刘慈欣演讲
刘慈欣不无批评地写道(http://cn.chinadaily.com.cn/2018-11/12/content_37243853.htm): 科幻的想象力由克拉克的广阔和深远,变成赛博朋克的狭窄和 ...
- Linux下日志文件监控系统Logwatch的使用记录
Linux下日志文件监控系统Logwatch的使用记录 原文:http://www.cnblogs.com/kevingrace/p/6519504.html 在维护Linux服务器时,经常需要查看系 ...
- 我java学习时的模样(一)
学会敲键盘,能够实现盲打 程序员写代码,是通过键盘将程序输入到编辑器中,而码子的高效,能够让自己的思路更流畅一些.如果想从事IT工作,那面打字就必须得会,并且,如果还一个一个字母去找,上司就会认为是一 ...
- WPF获取程序启动路径(StartupPath)
1. 在传统的Winform中获取 可以使用: Application.StartupPath Application.ExecutablePath 很可惜,这些方法,在WPF中都失效啦 2. 在WP ...
- Gradient descent and others
Batch gradient descent Procedure 在循环中跌倒公式\(\theta_j:=\theta_j-\alpha{1\over{m}}\sum_{i=1}^m(h_{\thet ...
- jmeter(1)——环境部署及安装
公司人事还有老大都找我谈了一下2019的目标和技能成长规划,所以整体想了一下,技能方面,自己今年准备从性能测试开始着手,也去咨询了一下大神,切入点最好是工具.性能测试是一门非常庞大的课程,最初级,最入 ...
- Scrapy框架学习(三)Spider、Downloader Middleware、Spider Middleware、Item Pipeline的用法
Spider有以下属性: Spider属性 name 爬虫名称,定义Spider名字的字符串,必须是唯一的.常见的命名方法是以爬取网站的域名来命名,比如爬取baidu.com,那就将Spider的名字 ...
- grep常用命令总结与基础正则
grep 'str'用来选取含有str的行 参数: -v 反向选取 -n 显示行号 -c 计算查找到字符串的次数 -i 选取时不区分大小写 基础正则表达式符: [list] 从字符集合里选出任意一个字 ...