zoukankan      html  css  js  c++  java
  • 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代码:

     1 // O(n^2) with pruning  
     2 public int largestRectangleArea1(int[] height) {  
     3   // Start typing your Java solution below  
     4   // DO NOT write main() function  
     5   int area = 0;  
     6   for (int i = 0; i < height.length; i++) {  
     7     for (int k = i + 1; k < height.length; k++) {  
     8       if (height[k] < height[k - 1]) {  
     9         i = k - 1;  
    10         break;  
    11       } else {  
    12         i = k;  
    13       }  
    14     }  
    15     int lowest = height[i];  
    16     for (int j = i; j >= 0; j--) {  
    17       if (height[j] < lowest) {  
    18         lowest = height[j];  
    19       }  
    20       int currArea = (i - j + 1) * lowest;  
    21       if (currArea > area) {  
    22         area = currArea;  
    23       }  
    24     }  
    25   }  
    26   return area;  
    27 }  

    虽然上面的解法可以过大集合,但是不是最优的方法,下面介绍使用栈的优化解法。时间复杂度为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;  
    }  
    

      

  • 相关阅读:
    synchronized关键字jvm实现及各种锁
    zookeeper选举算法
    git远程操作
    git分支的创建与分支之间合并的底层原理
    员工贷项目总结
    Python之字符串操作
    Python实现购物车的功能
    Python列表学习笔记
    Python中如何使用boolean类型的数据
    Python实现用户登录账户
  • 原文地址:https://www.cnblogs.com/avril/p/3278873.html
Copyright © 2011-2022 走看看