Largest Rectangle in Histogram

https://leetcode.com/problems/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(n^2)的解法很容易写出。遍历每个histogram,对其往左右两个方向拓展,直到遇到数组边缘或者比它小的数字,这样便找出了此处的窗口宽度。于是此处的面积,就是窗口宽度乘以该histogram的高度。遍历结束之后,全局最大的面积也就出来了。

但是这个解法在数据量大的时候会TLE,这个结果是可以预见的。所以,本题一定不是要O(n^2)的解法,应该是O(n)。直觉上像是动态规划的题目,但是想了半天都没找到解法。

看到stack的tag,想到利用height的递增性,但是还是想不出解法。好吧,只能瞄了瞄大神的解法。

思路是,始终维护一个单调递增的stack。一旦当前height比栈顶小,就弹出栈顶,直到当前height进栈后,是递增的。那么,弹出栈顶的时候,如何计算当前面积?我们令当前准备弹出的位置为index,那么之前栈内,index往右一定都比index高,所以往右的都是宽度(rightIndex - hereIndex)。从index往左,是不是一定都比index矮?不一定,比如上图,1、5、6就是的。但是,1、5、6、2、3,2进来的时候,5、6被弹出了,所以只有1、2、3。计算2的宽度的时候,我们看到从1到2之间的5、6都是可以算在2的宽度里的。所以往左的宽度就是hereIndex - stack.peek()。这里的stack.peel()其实就是在栈内的index的前一个坐标。

我们看到,(rightIndex - hereIndex) + (hereIndex - stack.peek())其实就是rightIndex - stack.peek(),就是在栈内,index左右两侧坐标相减。但是,如果index是栈内的最后一个元素,弹出后,栈内为空。index是全局最短的元素,从左边的0一直到右边都是它的宽度,所以此时宽度是rightIndex + 1。

当然,整个遍历结束后,还要对栈内剩余的元素进行一遍同样的操作。

public class Solution {

    public int largestRectangleArea(int[] height) {

        int result = 0, current = 0;

        Stack<Integer> stack = new Stack<Integer>();

        for(int i = 0; i < height.length; i++) {

            if(stack.empty() || height[stack.peek()] <= height[i]) {

                stack.push(i);

            } else {

                int rightIndex = stack.peek();

                while(!stack.empty() && height[stack.peek()] > height[i]) {

                    int hereIndex = stack.pop();

                    if(!stack.empty()) {

                        // current = height[hereIndex] * ((rightIndex - hereIndex) + (hereIndex - stack.peek()));

                        current = height[hereIndex] * (rightIndex - stack.peek());

                    } else {

                        // current = height[hereIndex] * ((rightIndex - hereIndex + 1) + hereIndex);

                        current = height[hereIndex] * (rightIndex + 1);

                    }

                    result = Math.max(result, current);

                }

                stack.push(i);

            }

        }

        if(!stack.empty()) {

            int rightIndex = stack.peek();

            while(!stack.empty()) {

                int hereIndex = stack.pop();

                if(!stack.empty()) {

                    current = height[hereIndex] * (rightIndex - stack.peek());

                } else {

                    current = height[hereIndex] * (rightIndex + 1);

                }

                result = Math.max(result, current);

            }

        }

        return result;

    }

}

这道题在思路上比较类似 Longest Valid Parentheses,都是维护了一个储存index的stack。对于栈内的任意一个index,栈顶元素是其窗口的右边界,而index左侧的元素则是窗口的左边界。面积的计算发生在栈顶元素弹出的时候。

因为每个元素都只被弹出一次,也仅仅计算一次,所以时间复杂度为O(n)。

这道题非常巧妙,也比较难。但是我们可以看出,stack这个数据结构并非像LIFO表面上这么简单。利用这个性质,加上单调性,本题可以得到解决。

你可能感兴趣的:(in)