最大矩形面积 | 豆包MarsCode AI刷题

119 阅读4分钟

学习方法与心得

解决算法问题时,理解题意、分析问题本质并选择合适的数据结构是高效解决问题的关键。对于这道最大矩形面积的问题,单调栈的应用是一个非常重要的技巧。它可以有效减少重复计算,提升时间效率。以下是我的学习心得:

  1. 深入理解单调栈的作用和实现,掌握其处理区间问题的核心思想。
  2. 在题解的过程中,要始终关注时间复杂度和空间复杂度的优化。
  3. 尝试分步解决问题,逐步调试代码,明确每一步的逻辑作用。

1. 题目解析

题目背景: 给定一个高度数组 h1, h2, ..., hN,每个元素表示一个高度。要求选取任意 k 个相邻元素,计算它们能形成的最大矩形面积。 对于 k 个相邻元素,其矩形最大面积定义为:

R(k)=k×min(h[i],h[i+1],...,h[i+k1])R(k)=k×min(h[i],h[i+1],...,h[i+k−1])

需要找出对于所有可能的 k 值,R(k) 的最大值。


2. 思路分析

为了高效解决问题,可以利用 单调栈 来优化:

  1. 核心目标

    :找到每个元素左右两侧第一个比它小的元素位置。

    • 左边界:第一个比当前元素小的左侧索引。
    • 右边界:第一个比当前元素小的右侧索引。
  2. 单调栈实现边界搜索

    • 左边界计算:从左向右遍历数组,栈中存储索引值,保证栈中元素从小到大排列。
    • 右边界计算:从右向左遍历数组,栈中存储索引值,保持栈中元素从小到大排列。
  3. 矩形面积计算

    • 对每个元素,矩形宽度为 right[i] - left[i] - 1,高度为当前元素值。
    • 更新所有可能的矩形面积,求得最大值。

时间复杂度

  • 单调栈遍历数组两次,每次为 O(n),因此总时间复杂度为 O(n)。

3. 代码详解

import java.util.*;
​
public class Main {
    // 计算最大矩形面积
    public static int solution(int n, int[] array) {
        // 定义左边界和右边界数组
        int[] left = new int[n];
        int[] right = new int[n];
​
        // 计算左边界
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < n; i++) {
            // 栈顶元素比当前元素大或相等时,出栈
            while (!stack.isEmpty() && array[stack.peek()] >= array[i]) {
                stack.pop();
            }
            // 栈为空表示左边没有更小的元素
            left[i] = stack.isEmpty() ? -1 : stack.peek();
            // 将当前元素索引压入栈
            stack.push(i);
        }
​
        // 计算右边界
        stack.clear(); // 清空栈以便重新利用
        for (int i = n - 1; i >= 0; i--) {
            // 栈顶元素比当前元素大或相等时,出栈
            while (!stack.isEmpty() && array[stack.peek()] >= array[i]) {
                stack.pop();
            }
            // 栈为空表示右边没有更小的元素
            right[i] = stack.isEmpty() ? n : stack.peek();
            // 将当前元素索引压入栈
            stack.push(i);
        }
​
        // 计算最大矩形面积
        int max = 0;
        for (int i = 0; i < n; i++) {
            // 矩形宽度
            int width = right[i] - left[i] - 1;
            // 矩形高度
            int area = width * array[i];
            // 更新最大面积
            max = Math.max(max, area);
        }
​
        return max;
    }
​
    // 测试用例
    public static void main(String[] args) {
        System.out.println(solution(5, new int[] { 1, 2, 3, 4, 5 }) == 9); // 输出:9
        System.out.println(solution(6, new int[] { 5, 4, 3, 2, 1, 6 }) == 9); // 输出:9
        System.out.println(solution(4, new int[] { 4, 4, 4, 4 }) == 16); // 输出:16
    }
}
​

代码解析

  1. 左边界计算

    • 遍历数组,维护一个单调递增的栈(存储索引)。
    • 每次遇到比栈顶元素小的元素时,弹出栈顶,找到左边第一个比当前元素小的索引。
  2. 右边界计算

    • 从数组右侧遍历,维护一个单调递增的栈。
    • 每次遇到比栈顶元素小的元素时,弹出栈顶,找到右边第一个比当前元素小的索引。
  3. 矩形面积计算

    • 对每个元素,矩形宽度为 right[i] - left[i] - 1,高度为当前元素值。
    • 计算面积并更新最大面积。

4. 学习总结与经验

  1. 单调栈的优势: 单调栈是一种高效解决区间问题的工具。在这道题中,它帮助我们快速找到左右两侧的边界,避免了重复计算,显著优化了时间复杂度。

  2. 代码调试技巧

    • 通过打印 leftright 数组,逐步验证单调栈的正确性。
    • 在调试过程中,确保边界条件(如栈为空时的处理)正确无误。
  3. 优化思想: 通过分解问题,将复杂的矩形面积问题拆分为边界搜索与面积计算两个子问题,降低了实现难度。


5. 学习方法与建议

  1. 理解单调栈: 通过练习多个涉及单调栈的问题(如直方图最大矩形、雨水接水问题),掌握其核心思想与应用场景。
  2. 逐步分解问题: 对于复杂问题,分解为多个子问题。先解决子问题,再组合实现整体功能。
  3. 时间复杂度优化: 在编码时,关注算法复杂度,通过分析关键步骤(如循环次数、栈操作)优化性能。
  4. 多做练习: 动手实现类似问题的代码,如柱状图的最大矩形面积和滑动窗口的最大值问题,巩固所学知识。

通过上述方法,可以更高效地解决类似的算法问题,同时加深对单调栈这一数据结构的理解和掌握。