leetocode做题心得83(柱状图中最大的矩形)

柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

84_1.png

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。


84_2.png

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

题解思路1(我的暴力法思路):

遍历数组,以当前数字的大小作为高度,往左以及往右寻找矩形的底的边界,确定矩形的面积,然后更新最大矩形面积。注意的是,当nums[i]==nums[i-1]时,这两者的矩形面积一定是相同的,可以跳过。

代码:

class Solution {
     public int largestRectangleArea(int[] heights) {
        int max_area=0;
        int curr_area;
        int len=heights.length;
        for(int i=0;i0&&heights[i]==heights[i-1]){continue;}
            int height=heights[i];
            //使用两个while循环确定以当前数字为高度的矩形底的长度
            int j=i;
            while(j-1>=0&&heights[j-1]>=height){
                j--;
            }
            int m=i;
            while(m+1<=len-1&&heights[m+1]>=height){
                m++;
            }
            curr_area=(m-j+1)*height;
            max_area=(max_area>curr_area)?max_area:curr_area;
        }
        return max_area;
    }
}

题解思路2:单调栈

1.我们依然需要寻找每一个高度它的左边界和右边界,但是我们可以换一种方式来寻找,即利用单调栈,单调栈顾名思义即栈中数据单调增或单调减的栈,想象一下一个单调递增的栈,我们从左到右遍历数组的过程中,当栈为空时元素直接入栈,当栈不为空时,入栈的操作需要先将栈中大于当前需入栈元素的所有元素依次出栈直到栈顶元素小于当前需入栈的元素,然后入栈。我们用一个标号来记录每一个入栈元素在数组中的次序,栈中j0紧挨j1下方时,也即代表了j1的左边界即为j0,j0是j1左侧的第一个小于它的元素。
2.利用单调栈一共只需要遍历两次数组,一次从左往右,一次从右往左,分别得出左边界和右边界,在一次遍历过程中这些数最多出入栈一次,所以时间复杂度是O(n)区别于暴力法的O(n^2)。

例子

我们用一个具体的例子 [6,7,5,2,4,5,9,3] 来帮助读者理解单调栈。我们需要求出每一根柱子的左侧且最近的小于其高度的柱子。初始时的栈为空。

我们枚举 6,因为栈为空,所以 66 左侧的柱子是「哨兵」,位置为 -1。随后我们将 6 入栈。

栈:[6(0)]。(这里括号内的数字表示柱子在原数组中的位置)
我们枚举 7,由于 6<7,因此不会移除栈顶元素,所以 7 左侧的柱子是 6,位置为 0。随后我们将 7 入栈。

栈:[6(0), 7(1)]
我们枚举 5,由于 7≥5,因此移除栈顶元素 7。同样地,6≥5,再移除栈顶元素 6。此时栈为空,所以 5 左侧的柱子是「哨兵」,位置为 −1。随后我们将 5 入栈。

栈:[5(2)]
接下来的枚举过程也大同小异。我们枚举 2,移除栈顶元素 5,得到 2 左侧的柱子是「哨兵」,位置为 −1。将 2 入栈。

栈:[2(3)]
我们枚举 4,5 和 9,都不会移除任何栈顶元素,得到它们左侧的柱子分别是 2,4 和 5,位置分别为 3,4 和 5。将它们入栈。

栈:[2(3), 4(4), 5(5), 9(6)]
我们枚举 3,依次移除栈顶元素 9,5 和 4,得到 3 左侧的柱子是 2,位置为 3。将 3 入栈。

栈:[2(3), 3(7)]
这样以来,我们得到它们左侧的柱子编号分别为 [−1,0,−1,−1,3,4,5,3]。用相同的方法,我们从右向左进行遍历,也可以得到它们右侧的柱子编号分别为 [2, 2, 3, 8, 7, 7, 7, 8],这里我们将位置 8 看作「哨兵」。

在得到了左右两侧的柱子之后,我们就可以计算出每根柱子对应的左右边界,并求出答案了。

代码:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] left = new int[n];
        int[] right = new int[n];
        
        Stack mono_stack = new Stack();
        for (int i = 0; i < n; ++i) {
            while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
                mono_stack.pop();
            }
            left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
            mono_stack.push(i);
        }

        mono_stack.clear();
        for (int i = n - 1; i >= 0; --i) {
            while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
                mono_stack.pop();
            }
            right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
            mono_stack.push(i);
        }
        
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
        }
        return ans;
    }
}

你可能感兴趣的:(leetocode做题心得83(柱状图中最大的矩形))