LeetCode Top 100 Liked Questions 84. Largest Rectangle in Histogram (Java版; Hard)

welcome to my blog

LeetCode Top 100 Liked Questions 84. Largest Rectangle in Histogram (Java版; Hard)

题目描述
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.

Example:

Input: [2,1,5,6,2,3]
Output: 10
class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        if(n==0){
            return 0;
        }
        Stack<Integer> s = new Stack<>();
        s.push(-1);
        int max = 0;
        //
        for(int i=0; i<n; i++){
            while(s.size()!=1 && heights[i] < heights[s.peek()]){
                int j = s.pop();
                max = Math.max(max, (i-s.peek()-1)*heights[j]);
            }
            s.push(i);
        }
        //栈顶元素一定是heights[n-1]
        //
        while(s.size()!=1){
            int j = s.pop();
            max = Math.max(max, (n-s.peek()-1)*heights[j]);
        }
        return max;
    }
}
第一次做, 分治算法; 核心: 最大面积矩形是以下三种情况中的某一种, (1)以最矮柱子为高,向两端扩展的矩形, (2)最矮柱子左边的最大矩形, (3)最矮柱子右边的最大矩形; 我用分治法的时候考虑了这么一种情况, 如果数组只有两个元素, 最小值时靠右的那个, 再次递归时, 有左部分, 但是没有右部分了, 所以我提前判断了是否有左右部分, 不过也可以直接使用if(left>right)这个递归终止条件, 不用提前判断是否有左右部分; 844ms
复杂度分析

时间复杂度:平均开销:O(nlogn); 最坏情况:O(n^2)

如果数组中的数字是有序的,分治算法将没有任何优化效果。

空间复杂度:O(n)。最坏情况下递归需要 O(n) 的空间。
class Solution {
    public int largestRectangleArea(int[] heights) {
        if(heights==null || heights.length==0)
            return 0;
        int res = Core(heights, 0, heights.length-1);
        return res;
    }
    public int Core(int[] heights, int left, int right){
        //base case
        if(left==right)
            return heights[left];
        int minIndex=left;
        for(int i=left+1; i<=right; i++)
            minIndex = heights[i] < heights[minIndex] ? i : minIndex;
        int curr = (right - left + 1) * heights[minIndex];
        int leftArea=0, rightArea = 0;
        //如果有左部分的话
        if(minIndex-1>=left)
            leftArea = Core(heights, left, minIndex-1);
        //如果有右部分的话
        if(minIndex+1<=right)
            rightArea = Core(heights, minIndex+1, right);
        return Math.max(Math.max(curr, leftArea), rightArea);
    }
}
第一次做, 单调栈:栈底到栈顶递增; 遍历阶段, 清算阶段; 栈中存索引; 初始时栈中压入-1, 为的是计算栈中倒数第二个元素对应的矩形面积; 以弹出的元素对应的高度作为矩形的高度进行计算; 注意遍历阶段和清算阶段面积公式的差别; 和LeetCode32(Hard)很像; 29ms
/*
这是道好题, 和LC32的栈解法有类似的地方, 这两道题的栈解法都需要先往栈中压入一个参考, 方便计算宽度; 
区别是LC32会更新参考值, 本题一直以-1作为参考值
实际上也是一种单调栈结构, 具有遍历阶段和清算阶段这两个阶段
*/
import java.util.Stack;

class Solution {
    public int largestRectangleArea(int[] heights) {
        if(heights==null || heights.length==0)
            return 0;
        //栈中压入的是索引
        Stack<Integer> s = new Stack<>();
        //压入参考值, 方便计算宽度
        s.push(-1);
        //单调栈:栈底到栈顶递增; 包含重复值, 重复值正常入栈就行
        //遍历阶段
        int res=0, curr, index;
        for(int i=0; i<heights.length; i++){
            if(s.size()==1)
                s.push(i);
            else{
                if(heights[i] >= heights[s.peek()]){
                    s.push(i);
                }
                else{
                    while(s.size()!=1 && heights[s.peek()] > heights[i]){
                        //计算以当前柱子高度作为高的矩形的面积
                        index = s.pop();
                        curr = (i - s.peek() - 1) * heights[index];
                        res = Math.max(res, curr);
                    }
                    s.push(i);
                }
            }
        }
        //清算阶段
        //该阶段, 栈中每个索引都满足: 从自己到末尾heights.length-1的范围中, 自己对应的高度最低,  发现这一点非常重要!!
        while(s.size()!=1){
            index = s.pop();
            /*
            细节: 括号内减去的是s.peek(), 这里体现了最开始压入-1的作用
            具体来说, 输入的高度是{2,1,5,6,2,3},清算阶段,栈中还剩(-1,1,4,5)
            弹出5的时候, curr = (heights.length-1 - 4)*heights[5]
            弹出4的时候, curr = (heights.length-1 - 1)*heights[4]
            弹出1的时候, curr = (heights.length-1 - (-1))*heights[1]
            清算阶段,栈中倒数第二个索引对应的高度是所有高度中的最小值, 对应的面积是数组的长度乘栈中倒数第二个元素对应的高度
            */
            curr = (heights.length - 1 - s.peek()) * heights[index];
            res = Math.max(res, curr);
        }
        return res;
    }
}
题解, 单调栈解法, 写的比我简洁很多
public class Solution {
    public int largestRectangleArea(int[] heights) {
        Stack < Integer > stack = new Stack < > ();
        stack.push(-1);
        int maxarea = 0;
        for (int i = 0; i < heights.length; ++i) {
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i])
                maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1));
            stack.push(i);
        }
        while (stack.peek() != -1)
            maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() -1));
        return maxarea;
    }
}
题解, 分支算法, 主要注意递归终止条件; 我用分治法的时候考虑了这么一种情况, 如果数组只有两个元素, 最小值时靠右的那个, 再次递归时, 有左部分, 但是没有右部分了, 所以我提前判断了是否有左右部分, 不过也可以直接使用if(left>right)这个递归终止条件, 不用提前判断是否有左右部分
public class Solution {
    public int calculateArea(int[] heights, int start, int end) {
        if (start > end)
            return 0;
        int minindex = start;
        for (int i = start; i <= end; i++)
            if (heights[minindex] > heights[i])
                minindex = i;
        return Math.max(heights[minindex] * (end - start + 1), Math.max(calculateArea(heights, start, minindex - 1), calculateArea(heights, minindex + 1, end)));
    }
    public int largestRectangleArea(int[] heights) {
        return calculateArea(heights, 0, heights.length - 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值