代码随想录刷题day60|柱状图中的最大矩形

本文介绍了如何使用双指针法和单调栈法解决柱状图中寻找最大矩形的问题,详细解释了两种方法的思路、代码实现及其优化过程,以及为何在首尾添加0的重要性。
摘要由CSDN通过智能技术生成


day60学习内容

day60主要内容

  • 柱状图中的最大矩形

声明
本文思路和文字,引用自《代码随想录》


一、柱状图中的最大矩形

84.原题链接
本题可以用双指针法和单调栈法解决。

1.1、双指针法

1.1.1、思路

对于每个条形,使用两个指针分别向左和向右移动,以确定可以延伸的最远距离,直到遇到更矮的条形。

  1. 循环每个条形:对于直方图中的每个条形,都单独计算以它为高的可能的最大矩形面积。
  2. 向左扩展:对于当前条形,向左移动指针,直到找到第一个比当前条形低的条形,记录左边界。
  3. 向右扩展:类似地,向右移动指针,直到找到第一个比当前条形低的条形,记录右边界。
  4. 计算面积:使用当前条形的高度与计算出的宽度(右边界减左边界加一)乘积得到面积,并更新最大面积。

1.1.2、代码–提交超时,过不了

class Solution {
    public int largestRectangleArea(int[] heights) {
        int maxArea = 0; // 用于记录找到的最大面积
        int n = heights.length; // 直方图的条形数量

        // 遍历每个条形,计算以当前条形为高的最大矩形面积
        for (int i = 0; i < n; i++) {
            int height = heights[i]; // 当前条形的高度

            // 向左扩展,直到遇到更矮的条形
            int left = i;
            while (left > 0 && heights[left - 1] >= height) {
                left--;
            }

            // 向右扩展,直到遇到更矮的条形
            int right = i;
            while (right < n - 1 && heights[right + 1] >= height) {
                right++;
            }

            // 计算当前条形可以扩展的最大面积
            int width = right - left + 1; // 计算宽度
            maxArea = Math.max(maxArea, height * width); // 更新最大面积
        }

        return maxArea; // 返回找到的最大面积
    }
}

1.1.3、代码–优化版本,可以提交成功

1、变量初始化

  • maxArea:用来记录和更新找到的最大矩形面积。
  • n:直方图中条形的数量。
  • leftright:分别用于存储每个条形的左边界和右边界。

2、计算左边界 (left 数组)

  • 对于数组中的每个条形,向左检查所有的条形以确定当前条形的左边界。如果左边的条形高度大于或等于当前条形的高度,那么它不能作为左边界。
  • 使用一个指针 p 从当前位置向左移动,如果遇到的条形高度不小于当前条形的高度,那么继续向左移动,直到找到一个较低的条形或到达数组的开始。
  • 使用 left[p] 直接跳跃到已知的边界位置,这是一个优化步骤,可以减少不必要的遍历。

3、计算右边界 (right 数组)

  • 与计算左边界类似,但是从直方图的右侧开始,向右检查所有的条形以确定当前条形的右边界。
  • 使用一个指针 p 从当前位置向右移动,如果遇到的条形高度不小于当前条形的高度,那么继续向右移动,直到找到一个较低的条形或到达数组的末端。
  • 同样使用 right[p] 进行跳跃,这有助于快速找到右边界。

4、计算最大矩形面积

  • 遍历每个条形,使用预处理好的 leftright 数组,计算以当前条形为高的矩形的最大宽度。
  • 宽度计算为 right[i] - left[i] + 1(即右边界索引减去左边界索引再加1)。
  • 计算当前条形的面积,与之前记录的最大面积比较,更新 maxArea

5、返回结果

  • 返回计算得到的最大矩形面积 maxArea
class Solution {
    public int largestRectangleArea(int[] heights) {
        int maxArea = 0;  // 用来存储和更新找到的最大矩形面积
        int n = heights.length;  // 直方图中条形的数量
        int[] left = new int[n];  // 存储每个条形的左边界
        int[] right = new int[n];  // 存储每个条形的右边界

        // 初始化左边界
        left[0] = 0;  // 第一个条形的左边界是它自己,因为左边没有其他条形
        for (int i = 1; i < n; i++) {
            int p = i - 1;  // 从当前条形的前一个条形开始检查
            while (p >= 0 && heights[p] >= heights[i]) {
                p = left[p] - 1;  // 使用已经计算的left数组进行“跳跃”,避免重复遍历
            }
            left[i] = p + 1;  // 确定当前条形的左边界
        }

        // 初始化右边界
        right[n - 1] = n - 1;  // 最后一个条形的右边界是它自己,因为右边没有其他条形
        for (int i = n - 2; i >= 0; i--) {
            int p = i + 1;  // 从当前条形的后一个条形开始检查
            while (p < n && heights[p] >= heights[i]) {
                p = right[p] + 1;  // 使用已经计算的right数组进行“跳跃”,避免重复遍历
            }
            right[i] = p - 1;  // 确定当前条形的右边界
        }

        // 计算最大面积
        for (int i = 0; i < n; i++) {
            // 计算以当前条形为高的最大矩形面积
            maxArea = Math.max(maxArea, heights[i] * (right[i] - left[i] + 1));
        }

        return maxArea;  // 返回计算得到的最大面积
    }
}

1.2、单调栈法

1.2.1、思路

  1. 初始化一个辅助的直方图:在原始的高度数组的两侧添加高度为0的条形,这样做的目的是确保所有原始的条形都能在处理时找到左右边界。

  2. 遍历直方图:从左到右遍历扩展后的直方图,利用栈来记录可能的左边界。

  3. 使用栈处理每个条形:对于每个条形,如果它的高度大于栈顶的条形,就将它的索引入栈。如果遇到的条形高度小于栈顶的条形,说明已经找到了栈顶条形的右边界,这时可以弹出栈顶,计算以栈顶条形为高度的矩形面积,直到栈顶的条形高度不再大于当前条形。

  4. 计算面积:在找到左右边界后,可以计算当前条形能形成的最大矩形的面积,更新最大面积的记录。

  5. 返回结果:遍历完成后,返回记录的最大面积值。

1.2.2、代码

class Solution {
    int largestRectangleArea(int[] heights) {
        Stack<Integer> st = new Stack<Integer>();
        
        // 扩展原始高度数组,添加0作为边界,以便处理边缘条形
        int [] newHeights = new int[heights.length + 2];
        newHeights[0] = 0;
        newHeights[newHeights.length - 1] = 0;
        for (int index = 0; index < heights.length; index++){
            newHeights[index + 1] = heights[index];
        }

        // 更新高度数组,使用新的包含边界的数组
        heights = newHeights;
        
        // 初始化栈,并压入第一个元素的索引,这里是0(高度为0的边界条形)
        st.push(0);
        int result = 0;  // 用于存储最大面积

        // 遍历所有的条形
        for (int i = 1; i < heights.length; i++) {
            // 如果当前条形高度大于栈顶条形高度,直接入栈
            if (heights[i] > heights[st.peek()]) {
                st.push(i);
            } 
            // 如果当前条形高度等于栈顶条形高度,替换栈顶(这样可以保持最新的索引)
            else if (heights[i] == heights[st.peek()]) {
                st.pop(); 
                st.push(i);
            } 
            // 如果当前条形高度小于栈顶条形高度,计算可能的最大面积
            else {
                while (!st.isEmpty() && heights[i] < heights[st.peek()]) {
                    int mid = st.peek(); // 弹出的栈顶元素,这是当前要计算面积的条形的高度
                    st.pop(); // 弹出栈顶元素
                    int left = st.peek(); // 新的栈顶元素是当前弹出元素的左边界
                    int right = i; // 当前索引是右边界
                    int w = right - left - 1; // 计算宽度
                    int h = heights[mid]; // 条形的高度
                    result = Math.max(result, w * h); // 更新最大面积
                }
                // 处理完所有右边界后,将当前条形入栈
                st.push(i);
            }
        }
        return result; // 返回最大面积
    }
}
1.2.2.1、为什么首尾要加0
  1. 确保所有条形都能找到边界

    • 当你使用单调栈来计算每个条形的最大矩形面积时,需要确定每个条形的左右边界。具体地说,左边界是栈中当前条形左边的第一个比它低的条形,右边界是第一个比它低的右侧条形。
    • 通过在数组首尾各添加一个高度为0的条形,可以保证所有原始直方图中的条形在栈操作中自然地找到其边界。例如,左边界的计算不会因为栈为空而导致错误,因为最左边的0始终在栈底;同理,最右边的0条形则自然成为所有条形的右边界。
  2. 自动处理栈中剩余元素

    • 在遍历结束时,栈中可能还剩余一些条形的索引,这些条形的右边界就是数组的末尾(最后一个0的位置)。添加了0之后,算法可以继续运行,并自动地处理这些剩余条形的面积计算,而无需编写额外的代码来特别处理它们。
    • 这意味着在整个数组的遍历过程中,每个条形都会被考虑到,并且当它从栈中弹出时,可以直接使用当前遍历到的位置作为其右边界。

总结

1.感想

  • 60天完结撒花。。

2.思维导图

本文思路引用自代码随想录,感谢代码随想录作者。

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值