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(直方图最大面积)的更多相关文章

  1. 【LeetCode】84. Largest Rectangle in Histogram——直方图最大面积

    Given n non-negative integers representing the histogram's bar height where the width of each bar is ...

  2. [LeetCode] Largest Rectangle in Histogram 直方图中最大的矩形

    Given n non-negative integers representing the histogram's bar height where the width of each bar is ...

  3. [LeetCode] Largest Rectangle in Histogram O(n) 解法详析, Maximal Rectangle

    Largest Rectangle in Histogram Given n non-negative integers representing the histogram's bar height ...

  4. leetcode Largest Rectangle in Histogram 单调栈

    作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4052343.html 题目链接 leetcode Largest Rectangle in ...

  5. LeetCode: Largest Rectangle in Histogram 解题报告

    Largest Rectangle in Histogram Given n non-negative integers representing the histogram's bar height ...

  6. [LeetCode] 84. Largest Rectangle in Histogram 直方图中最大的矩形

    Given n non-negative integers representing the histogram's bar height where the width of each bar is ...

  7. LeetCode 84. Largest Rectangle in Histogram 直方图里的最大长方形

    原题 Given n non-negative integers representing the histogram's bar height where the width of each bar ...

  8. [leetcode]84. Largest Rectangle in Histogram直方图中的最大矩形

    Given n non-negative integers representing the histogram's bar height where the width of each bar is ...

  9. [LeetCode] Largest Rectangle in Histogram 解题思路

    Given n non-negative integers representing the histogram's bar height where the width of each bar is ...

随机推荐

  1. 根据浏览器显示界面大小变换,替换css文件方法

    在1024屏幕下,选择适配1024屏幕的css文件, 在大于1024屏幕下,选择适配大屏幕的css文件. 在html中的head标签中引用css文件时,加入media属性. 例: <link r ...

  2. SQLAlchemy文档翻译

    想记录一下SQLAlchemy的入门学习,然后突发奇想觉得:为什么不直接翻译一下文档呢?于是顺手查了查怎么使用Gitbook,2333 于是就在Github开了项目,然后导入了Gitbook,开始写. ...

  3. 创建文本注记TextElement

    1.创建一个字体 /// <summary> /// 字体设置 /// </summary> /// <param name="size">Th ...

  4. bootstrap做了一个表格

    花了一下午做了一个表格: 大致是这样: 代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf ...

  5. Canvas绘制渐变

    1.绘制线性渐变 Canvas提供了用于创建线性渐变的函数createLinearGradient(x0,y0,x1,y1),坐标点(x0,y0)是起点 ,(x1,y1)是终点 创建一个渐变色 var ...

  6. 深入理解javascript原型和闭包(6)——继承

    为何用“继承”为标题,而不用“原型链”? 原型链如果解释清楚了很容易理解,不会与常用的java/C#产生混淆.而“继承”确实常用面向对象语言中最基本的概念,但是java中的继承与javascript中 ...

  7. vtkAnimationCue、vtkCommand和vtkAVIWriter

    1. 用vtkAnimationCue自定义一个vtkCustomAnimationCue类,用来实现球体逐渐张开的过程: 2.用vtkCommand自定义衍生一个vtkCustomAnimation ...

  8. mysql 存储过程在批处理数据中的应用

    最近批处理数据的时候,突然想到:为什么不使用存储过程进行数据批处理? 为什么要进行批处理? 自答:减少数据库连接次数,提高效率. 存储过程批处理数据的优点:一次编译,永久执行. 这次的批处理逻辑较简单 ...

  9. C#给其他程序发消息

    1.相关声明函数,SendMessage可定义两种格式. [DllImport("User32.DLL", CharSet = CharSet.Auto)]public stati ...

  10. nyoj220 推桌子(贪心算法)

    这道题太坑了,from 和to有可能写反,还得正过来: 推桌子 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 The famous ACM (Advanced Co ...