最大矩形面积问题的深入解析:基于栈的优化解法
问题描述
在一组高度数组中,找到任意连续的 k 个元素能够构成的最大矩形面积。
输入:
- 一个数组表示高度。
- 输出矩形的最大面积。
解题思路
对于这个问题,我们可以借助单调栈的思想来优化解法。以下是实现过程的详细分析。
解法概要
- 利用单调栈维护一个递增高度序列。
- 当遇到当前高度小于栈顶高度时,计算以栈顶高度为最小值的矩形面积。
- 在数组首尾添加哨兵值(高度为0),确保所有柱体都能计算。
详细步骤
-
数组预处理:
- 为避免处理边界问题,在数组前后分别添加高度为
0的哨兵值。 - 新数组长度为
n + 2。
- 为避免处理边界问题,在数组前后分别添加高度为
-
遍历高度数组:
- 利用栈存储柱状图的索引。
- 如果当前高度小于栈顶高度,则开始计算矩形面积。
-
计算面积:
- 弹出栈顶索引,设其高度为矩形最小高度。
- 宽度为当前索引与栈顶索引的差值减
1。 - 更新最大面积。
-
最终结果:
- 遍历结束后,最大面积即为结果。
代码实现
import java.util.Stack;
public class Main {
public static int solution(int n, int[] heights) {
// 创建一个栈,保存每个高度的索引
Stack<Integer> stack = new Stack<>();
int maxArea = 0;
// 为了处理边界情况,在原数组前后分别添加一个高度为0的哨兵元素
int[] newHeights = new int[n + 2];
System.arraycopy(heights, 0, newHeights, 1, n);
// 遍历所有的柱状图高度
for (int i = 0; i < newHeights.length; i++) {
// 如果当前高度比栈顶的高度小,说明可以计算以栈顶高度为最小值的面积
while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
int h = newHeights[stack.pop()]; // 栈顶元素作为高度
int width = i - stack.peek() - 1; // 计算宽度
maxArea = Math.max(maxArea, h * width); // 更新最大面积
}
stack.push(i); // 当前索引入栈
}
return maxArea;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}) == 9);
System.out.println(solution(5, new int[]{5, 4, 3, 2, 1}) == 9);
System.out.println(solution(6, new int[]{2, 1, 5, 6, 2, 3}) == 10);
}
}
代码分析
-
单调栈的维护:
- 栈中存储的是高度的索引,并且对应的高度保持递增。
- 遇到更小的高度时,弹出栈顶进行面积计算。
-
面积计算的核心公式:
- 高度:弹出栈顶的高度。
- 宽度:
i - stack.peek() - 1,其中i是当前索引,stack.peek()是栈新的栈顶。 - 面积:
height * width。
-
哨兵的作用:
- 在数组前后加入高度为
0的哨兵,确保可以计算所有柱体的面积。
- 在数组前后加入高度为
-
时间复杂度:
- 每个元素最多被压入和弹出一次,时间复杂度为
O(n)。
- 每个元素最多被压入和弹出一次,时间复杂度为
测试用例解析
用例1:
-
输入:
[1, 2, 3, 4, 5] -
过程:
- 栈变化:
[1] -> [1,2] -> [1,2,3] -> [1,2,3,4] -> [1,2,3,4,5] -> ... - 面积计算:
5*1=5, 4*2=8, 3*3=9。
- 栈变化:
-
输出:
9
用例2:
-
输入:
[5, 4, 3, 2, 1] -
过程:
- 栈变化:逐渐弹出较大的高度。
- 面积计算:
5*1=5, 4*2=8, 3*3=9。
-
输出:
9
用例3:
-
输入:
[2, 1, 5, 6, 2, 3] -
过程:
- 栈变化:弹出较小高度,逐步计算面积。
- 面积计算:
6*1=6, 5*2=10。
-
输出:
10
个人思考与扩展
-
单调栈的适用场景:
-
单调栈适合解决连续性子问题,如:
- 柱状图最大矩形面积。
- 下一个更大元素的问题。
-
-
代码优化:
- 当前实现清晰,但可以通过内存池优化栈存储以节省空间。
-
与其他算法的对比:
- 动态规划:处理类似问题时会保存更多中间状态,适合多次查询场景。
- 暴力法:复杂度
O(n^2),仅适合小规模数据。
单调栈是一个强大的工具,在解决矩形面积和类似问题时,不仅效率高,还能确保代码逻辑清晰易懂。这种方式值得在更多场景中推广。