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

142 阅读4分钟

学习方法与心得

在解决算法问题时,理解问题的核心、分析问题本质,并选择合适的数据结构是高效解决问题的关键。在这道关于最大矩形面积的问题中,单调栈的应用是至关重要的技巧。它能够显著减少重复计算,从而提升程序的运行效率。以下是我在学习过程中的一些心得:

  1. 理解单调栈的作用:深入理解单调栈如何工作,掌握它在处理区间问题时的核心思想。
  2. 优化时间和空间复杂度:在解题过程中,始终关注如何提高程序的时间和空间效率。
  3. 逐步调试与分步解决问题:在编写代码时,可以通过分解问题、逐步调试来明确每一部分的逻辑作用。

1. 题目解析

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

R(k)=k×min⁡(h[i],h[i+1],...,h[i+k−1])R(k) = k \times \min(h[i], h[i+1], ..., h[i+k-1])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. 代码详解

java
複製程式碼
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. 加强实践
    通过不断地练习和实现类似问题的代码,逐渐加深对数据结构和算法的理解与应用。