leetcode 84.Largest Rectangle in Histogram(直方图中的最大矩形)

本文介绍了一种寻找直方图中最大矩形面积的高效算法。通过使用栈来跟踪元素,算法能在O(n)的时间复杂度内解决该问题。文章详细解释了算法的逻辑,包括如何处理升序和非升序区间,以及如何计算最大面积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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].

给出一个数组,判断其构成的直方图的最大矩形面积

思路:
因为要求是矩形面积,短边会限制面积,为了更好地判断短边,所以最好是升序排列,
但是不能排序,因为排序会影响整个形状。
所以采用区间升序,升序区间结束的时候计算升序部分的最大面积

升序部分区间的面积计算可以从左到右,也可以从右到左

比如[1,2,3], 长度是3
从左到右:1* 3 = 3, 2* 2 = 4, 3* 1 = 3 最大面积是4
从右到左:3* 1 = 3, 2* 2 = 4, 1* 3 = 3 最大面积是4

因为是区间升序,比如[1,2,3,1], 升序区间是[1,2,3],
从右到左计算完的元素移出,直到剩下的元素对右边的1不再满足升序条件,这里计算完3,2后移出,直到剩下[1,1],为什么移出,因为短边的限制,所以以后用不到比左右都大的2,3了

因为每次都要和升序区间最右边的元素比较,所以用stack

用stack来保存index,可以通过index取得高度和宽度
stack为空或者新元素>stack.peek()的时候(放入stack后满足升序条件),将新元素push进stack

index到数组最右边超出边界的时候,假定index指向的高度是0,处理剩下所有stack中的元素

举个例子:
input = [1,2,3,1] index=3,stack元素:0,1,2 (保存的是index)
stack最上面:input[2] = 3 > 1 所以pop 出2
计算input[2]* (index - 3左边的index:stack.peek - 1) = 3* (3 - 1 - 1) = 3
以此类推直到剩下1,不再满足升序,所以push(1 (index=3的元素) )

然后index移到input右边界外,index=4,这时处理剩下的[1,1]
注意剩下最后一个1时,1左边已经没有元素,假设左边的index是-1,
那么宽度=index- (-1) -1 = index

    public int largestRectangleArea(int[] heights) {
        if (heights == null || heights.length == 0) {
            return 0;
        }
        
        int top = 0;
        int area = 0;
        int MaxArea = 0;
        Stack<Integer> stack = new Stack<>();
        
        for (int i = 0; i < heights.length; i++) {
            if (stack.isEmpty() || heights[i] >= heights[stack.peek()]) {
                stack.push(i);   
            } else {
                while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                    top = stack.pop();
                    if (stack.isEmpty()) {
                        area = heights[top] * i;
                    } else {
                        area = heights[top] * (i - stack.peek() - 1);
                    }
                    if (area > MaxArea) {
                        MaxArea = area;
                    }
                }
                stack.push(i);
            }            
        }
        
        while (!stack.isEmpty()) {
            top = stack.pop();
            if (stack.isEmpty()) {
                area = heights[top] * heights.length;
            } else {
                area = heights[top] * (heights.length - stack.peek() - 1);
            }
            
            if (area > MaxArea) {
                MaxArea = area;
            }
        }
        
        return MaxArea;
    }

简化代码如下
需要注意的地方是取出cur后,用heights[cur] * 矩形宽时,矩形宽是当前的i 减去上一个保存的index处再减1,而不是简单的i - cur
为什么会这样,举个例子
[1, 2, 3, 1]
stack中保存了下标0,1,2,遇到下标为3的1时处理掉下标1,2,再保存3,那么最后stack中仅有0,3,这时i = 4,计算下标3的面积时肯定应该宽为4-0-1=3,因为还应该包括之间值2,3所在的区域。
需要注意几点,写在代码注释中

    public int largestRectangleArea(int[] heights) {
        if(heights == null || heights.length == 0) {
            return 0;
        }

        Stack<Integer> stack = new Stack<>();
        int result = 0;

        for(int i = 0; i < heights.length; i++) {
            while(!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
              //必须先pop,因为不pop就无法查到前面的index
                int cur = stack.pop();
                //pop之后stack可能为空,所以要再验证stack是否为空
                result = Math.max(result, heights[cur] * (stack.isEmpty()?i:(i-stack.peek()-1)));
            }

            stack.push(i);
        }

       //index超过数组右边界时仍然需要处理stack中剩下的
        while(!stack.isEmpty()) {
            int cur = stack.pop();
            result = Math.max(result, heights[cur] * (stack.isEmpty()?heights.length:(heights.length-stack.peek()-1)));
        }

        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值