LeetCode: Largest Rectangle in Histogram(直方图最大面积)
http://blog.csdn.net/abcbc/article/details/8943485
具体的题目描述为:
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(n^2). 单纯的穷举在LeetCode上面过大集合时会超时。可以通过选择合适的右边界,做一个剪枝(Pruning)。观察发现当height[k] >= height[k - 1]时,无论左边界是什么值,选择height[k]总会比选择height[k - 1]所形成的面积大。因此,在选择右边界的时候,首先找到一个height[k] < height[k - 1]的k,然后取k - 1作为右边界,穷举所有左边界,找最大面积。
Java代码:
// O(n^2) with pruning
public int largestRectangleArea1(int[] height) {
// Start typing your Java solution below
// DO NOT write main() function
int area = 0;
for (int i = 0; i < height.length; i++) {
for (int k = i + 1; k < height.length; k++) {
if (height[k] < height[k - 1]) {
i = k - 1;
break;
} else {
i = k;
}
}
int lowest = height[i];
for (int j = i; j >= 0; j--) {
if (height[j] < lowest) {
lowest = height[j];
}
int currArea = (i - j + 1) * lowest;
if (currArea > area) {
area = currArea;
}
}
}
return area;
}
虽然上面的解法可以过大集合,但是不是最优的方法,下面介绍使用栈的优化解法。时间复杂度为O(n).
此解法的核心思想为:一次性计算连续递增的区间的最大面积,并且考虑完成这个区间之后,考虑其前、后区间的时候,不会受到任何影响。也就是这个连续递增区间的最小高度大于等于其前、后区间。
这个方法非常巧妙,最好通过一个图来理解:
假设输入直方图为:int[] height = {2,7,5,6,4}.
这个方法运行的时候,当遇到height[2] == 5的时候,发现其比之前一个高度小,则从当前值(5)开始,向左搜索比当前值小的值。当搜索到最左边(2)时,比5小,此时计算在height[0]和height[2]之间的最大面积,注意不包括height[0]和和height[2]。height[1]以红色标出的这个区域就被计算完成。同样的方法,计算出绿色和粉色的面积。
因此这个方法需要使用两个栈。第一个栈为高度栈heightStack,用于记录还没有被计算过的连续递增的序列的值。第二个栈为下标栈indexStack,用于记录高度栈中对应的每一个高度的下标,以计算宽度。
算法具体执行的步骤为:
若heightStack为空或者当前高度大于heightStack栈顶,则当前高度和当前下标分别入站(下面有一个解法可以只用一个栈即可,用栈来保存下标,而高度由下标很容易得到)。所以heightStack记录了一个连续递增的序列。
若当前高度小于heightStack栈顶,heightStack和indexStack出栈,直到当前高度大于等于heightStack栈顶。出栈时,同时计算区间所形成的最大面积。注意计算完之后,当前值入栈的时候,其对应的下标应该为最后一个从indexStack出栈的下标。比如height[2]入栈时,其对应下标入栈应该为1,而不是其本身的下标2。如果将其本身下标2入栈,则计算绿色区域的最大面积时,会忽略掉红色区域。
C++代码:
class Solution {
public:
int largestRectangleArea(vector<int> &height) {
if(height.size() == 0) return 0;
int res = 0;
vector<int> tmp = height;
tmp.push_back(0); // Important
stack<int> s;
for(int i = 0; i < tmp.size(); i++)
{
if(s.empty() || (!s.empty() && tmp[i] >= tmp[s.top()])) s.push(i);
else{
while(!s.empty() && tmp[s.top()] > tmp[i])
{
int idx = s.top(); s.pop();
int width = s.empty() ? i : (i-s.top()-1);
res = max(res, tmp[idx] * width);
}
s.push(i); // Important
}
}
return res;
}
};
Java代码:
// O(n) using two stacks
public int largestRectangleArea(int[] height) {
// Start typing your Java solution below
// DO NOT write main() function
int area = 0;
java.util.Stack<Integer> heightStack = new java.util.Stack<Integer>();
java.util.Stack<Integer> indexStack = new java.util.Stack<Integer>();
for (int i = 0; i < height.length; i++) {
if (heightStack.empty() || heightStack.peek() <= height[i]) {
heightStack.push(height[i]);
indexStack.push(i);
} else if (heightStack.peek() > height[i]) {
int j = 0;
while (!heightStack.empty() && heightStack.peek() > height[i]) {
j = indexStack.pop();
int currArea = (i - j) * heightStack.pop();
if (currArea > area) {
area = currArea;
}
}
heightStack.push(height[i]);
indexStack.push(j);
}
}
while (!heightStack.empty()) {
int currArea = (height.length - indexStack.pop()) * heightStack.pop();
if (currArea > area) {
area = currArea;
}
}
return area;
}
更新:
在网上发现另外一个使用一个栈的O(n)解法,代码非常简洁,栈内存储的是高度递增的下标。对于每一个直方图高度,分两种情况。1:当栈空或者当前高度大于栈顶下标所指示的高度时,当前下标入栈。否则,2:当前栈顶出栈,并且用这个下标所指示的高度计算面积。而这个方法为什么只需要一个栈呢?因为当第二种情况时,for循环的循环下标回退,也就让下一次for循环比较当前高度与新的栈顶下标所指示的高度,注意此时的栈顶已经改变由于之前的出栈。
Java代码:
// O(n) using one stack
public int largestRectangleArea(int[] height) {
// Start typing your Java solution below
// DO NOT write main() function
int area = 0;
java.util.Stack<Integer> stack = new java.util.Stack<Integer>();
for (int i = 0; i < height.length; i++) {
if (stack.empty() || height[stack.peek()] < height[i]) {
stack.push(i);
} else {
int start = stack.pop();
int width = stack.empty() ? i : i - stack.peek() - 1;
area = Math.max(area, height[start] * width);
i--;
}
}
while (!stack.empty()) {
int start = stack.pop();
int width = stack.empty() ? height.length : height.length - stack.peek() - 1;
area = Math.max(area, height[start] * width);
}
return area;
}
LeetCode: Largest Rectangle in Histogram(直方图最大面积)的更多相关文章
- 【LeetCode】84. 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 直方图中最大的矩形
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
- [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] 84. Largest Rectangle in Histogram 直方图中最大的矩形
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
- LeetCode 84. Largest Rectangle in Histogram 直方图里的最大长方形
原题 Given n non-negative integers representing the histogram's bar height where the width of each bar ...
- [leetcode]84. 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 解题思路
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
随机推荐
- 使用自定义tld标签简化jsp的繁琐操作
最近做一个树形结构的展示,请求目标页面后,后台只返回简单的List,虽然有想过在jsp页面内做一些操作简化,但是太繁琐了,其他的标签又不能满足需求,所以只能自己做一个.使用tld标签可以简化jsp代码 ...
- 前端模块化工具-webpack
详解前端模块化工具-webpack webpack是一个module bundler,抛开博大精深的汉字问题,我们暂且管他叫'模块管理工具'.随着js能做的事情越来越多,浏览器.服务器,js似乎无处不 ...
- elk系列8之logstash+redis+es的架构来收集apache的日志
preface logstash--> redis --> logstash --> es这套架构在讲究松耦合关系里面是最简单的, 架构图如下: 解释下这个架构图的流程 首先前端lo ...
- easyUi 页面创建一个toolbar实例
1.定义toolbar方法 pagination : true, pageSize : 10, pageList : [ 5, 10, 15, 20, 50 ], toolbar : toolbarF ...
- Android内存优化-内存泄漏的几个场景以及解决方式
转自:http://blog.csdn.net/a910626/article/details/50849760 一.什么是内存泄漏 在Java程序中,如果一个对象没有利用价值了,正常情况下gc是会对 ...
- vim编辑器的使用
I 在光标所在行的行首插入 A 在光标所在行的行尾插入 { 移动到上一段 } 移动到下一段 空格向后移动一格 H 屏幕顶部 M 屏幕中间 L 屏幕下方 n| 使光标移动到第几个字符处 ngg 移动到制 ...
- 搭建FTP服务器
yum install vsftpd -yyum install pam* db4* --skip-broken –y 创建并生成vsftpd 数据库文件vi /etc/vsftpd/ftpusers ...
- Oracle开机自启动
linux下启动oracle su - oracle #用oracle用户登陆 sqlplus /nolog conn /as sysdba startup exit lsnrctl start ex ...
- QT读取文本(字符串)最后一行的方法
QString str; QTextStream ts(&str); str = this->toPlainText(); ts.seek(str.lastIndexOf("- ...
- 学习笔记——k近邻法
对新的输入实例,在训练数据集中找到与该实例最邻近的\(k\)个实例,这\(k\)个实例的多数属于某个类,就把该输入实例分给这个类. \(k\) 近邻法(\(k\)-nearest neighbor, ...