柱状图中最大的矩形-leetcode

题目描述

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

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

示例 1:

img

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

img

输入: heights = [2,4]
输出: 4

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104

解法一

思路:

  1. 核心思想

我们要找到一个矩形,它的高度由某一个柱子 ii 的高度 heights[i] 决定。那么,这个矩形能向左右延伸多远呢?

  • 左边界:左边第一个比 heights[i] 的柱子。
  • 右边界:右边第一个比 heights[i] 的柱子。

一旦确定了左右边界,以 heights[i] 为高的矩形面积就是:

extArea=extheights[i]imes(ext右边界索引−ext左边界索引−1)extArea=extheights[i]imes(ext右边界索引−ext左边界索引−1)

  1. 为什么用单调栈?

单调栈可以帮助我们在一次遍历中,为每个柱子找到左右两边最近的比它矮的柱子。

  • 我们维持一个从栈底到栈顶单调递增的栈(存储柱子的下标)。
  • 当我们遇到一个比栈顶元素的柱子时,说明找到了栈顶元素的“右边界”。
  • 而栈顶元素在栈中的前一个元素,就是它的“左边界”。
  1. 算法步骤

为了处理边界情况(例如所有柱子都是递增的,或者栈中最后剩下的元素),我们通常在原数组的开头和结尾各加一个高度为 0 的“哨兵”柱子

  1. heights 数组前后各加一个 0
  2. 初始化一个空栈,先压入第一个哨兵(索引 0)。
  3. 从索引 1 开始遍历数组:
    • 如果当前柱子高度 大于等于 栈顶柱子高度:将当前索引入栈(保持单调递增)。
    • 如果当前柱子高度 小于 栈顶柱子高度:说明栈顶柱子的右边界找到了。
      • 弹出栈顶元素,记为 mid,其高度为 h = heights[mid]
      • 新的栈顶元素就是 mid 的左边界,记为 left = stack.top()
      • 当前遍历到的索引就是 mid 的右边界,记为 right = i
      • 计算面积:width = right - left - 1area = h * width
      • 更新最大面积。
      • 重复此过程,直到当前柱子不再小于栈顶柱子,然后将当前索引入栈。

代码:

public class Solution {
    public int largestRectangleArea(int[] heights) {
        // 1. 处理边界情况
        if (heights == null || heights.length == 0) {
            return 0;
        }

        // 2. 准备一个新的数组,首尾各加一个高度为 0 的“哨兵”
        // 哨兵的作用:
        // 左侧 0:保证第一个柱子入栈时,栈不为空,且左边界能被正确计算
        // 右侧 0:保证遍历结束时,栈内所有剩余的柱子都能被弹出并计算面积
        int[] newHeights = new int[heights.length + 2];
        newHeights[0] = 0;
        for (int i = 0; i < heights.length; i++) {
            newHeights[i + 1] = heights[i];
        }
        newHeights[newHeights.length - 1] = 0;

        // 3. 定义单调递增栈(存储的是索引)
        Deque<Integer> stack = new ArrayDeque<>();
        int maxArea = 0;

        // 4. 开始遍历
        for (int i = 0; i < newHeights.length; i++) {
            // 如果当前柱子高度小于栈顶柱子高度,说明栈顶柱子的右边界找到了
            while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
                // 弹出栈顶元素作为待计算矩形的高度
                int midIndex = stack.pop();
                int h = newHeights[midIndex];

                // 此时新的栈顶元素就是该柱子的左边界索引
                // 右边界索引就是当前的 i
                int leftIndex = stack.peek();
                int rightIndex = i;

                // 宽度 = 右边界 - 左边界 - 1
                int w = rightIndex - leftIndex - 1;
                
                // 更新最大面积
                maxArea = Math.max(maxArea, h * w);
            }
            // 当前索引入栈,保持栈内高度单调递增
            stack.push(i);
        }

        return maxArea;
    }

}
posted @ 2026-04-04 15:12  狐狸胡兔  阅读(2)  评论(0)    收藏  举报