学习方法与心得
解决算法问题时,理解题意、分析问题本质并选择合适的数据结构是高效解决问题的关键。对于这道最大矩形面积的问题,单调栈的应用是一个非常重要的技巧。它可以有效减少重复计算,提升时间效率。以下是我的学习心得:
- 深入理解单调栈的作用和实现,掌握其处理区间问题的核心思想。
- 在题解的过程中,要始终关注时间复杂度和空间复杂度的优化。
- 尝试分步解决问题,逐步调试代码,明确每一步的逻辑作用。
1. 题目解析
题目背景: 给定一个高度数组 h1, h2, ..., hN,每个元素表示一个高度。要求选取任意 k 个相邻元素,计算它们能形成的最大矩形面积。 对于 k 个相邻元素,其矩形最大面积定义为:
需要找出对于所有可能的 k 值,R(k) 的最大值。
2. 思路分析
为了高效解决问题,可以利用 单调栈 来优化:
-
核心目标
:找到每个元素左右两侧第一个比它小的元素位置。
- 左边界:第一个比当前元素小的左侧索引。
- 右边界:第一个比当前元素小的右侧索引。
-
单调栈实现边界搜索
:
- 左边界计算:从左向右遍历数组,栈中存储索引值,保证栈中元素从小到大排列。
- 右边界计算:从右向左遍历数组,栈中存储索引值,保持栈中元素从小到大排列。
-
矩形面积计算
:
- 对每个元素,矩形宽度为
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
}
}
代码解析
-
左边界计算:
- 遍历数组,维护一个单调递增的栈(存储索引)。
- 每次遇到比栈顶元素小的元素时,弹出栈顶,找到左边第一个比当前元素小的索引。
-
右边界计算:
- 从数组右侧遍历,维护一个单调递增的栈。
- 每次遇到比栈顶元素小的元素时,弹出栈顶,找到右边第一个比当前元素小的索引。
-
矩形面积计算:
- 对每个元素,矩形宽度为
right[i] - left[i] - 1,高度为当前元素值。 - 计算面积并更新最大面积。
- 对每个元素,矩形宽度为
4. 学习总结与经验
-
单调栈的优势: 单调栈是一种高效解决区间问题的工具。在这道题中,它帮助我们快速找到左右两侧的边界,避免了重复计算,显著优化了时间复杂度。
-
代码调试技巧:
- 通过打印
left和right数组,逐步验证单调栈的正确性。 - 在调试过程中,确保边界条件(如栈为空时的处理)正确无误。
- 通过打印
-
优化思想: 通过分解问题,将复杂的矩形面积问题拆分为边界搜索与面积计算两个子问题,降低了实现难度。
5. 学习方法与建议
- 理解单调栈: 通过练习多个涉及单调栈的问题(如直方图最大矩形、雨水接水问题),掌握其核心思想与应用场景。
- 逐步分解问题: 对于复杂问题,分解为多个子问题。先解决子问题,再组合实现整体功能。
- 时间复杂度优化: 在编码时,关注算法复杂度,通过分析关键步骤(如循环次数、栈操作)优化性能。
- 多做练习: 动手实现类似问题的代码,如柱状图的最大矩形面积和滑动窗口的最大值问题,巩固所学知识。
通过上述方法,可以更高效地解决类似的算法问题,同时加深对单调栈这一数据结构的理解和掌握。