💻 [LeetCode Hot100] 接雨水 动态规划 vs 双指针,Java实现!图解+代码,小白也能秒懂!
✏️本文对应题目链接:接雨水
📌 题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:可以接6个单位的雨水(蓝色部分表示雨水)。
🧠 解题思路(图文分解)
❗ 核心难点
如何高效计算每个位置能接的雨水量?
方法一:动态规划(黄金思路)✨
关键步骤:
- 预处理:
- 从左到右遍历,记录每个位置左侧的最大高度
leftMax - 从右到左遍历,记录每个位置右侧的最大高度
rightMax
- 从左到右遍历,记录每个位置左侧的最大高度
- 计算雨水量:
- 每个位置能接的雨水量 =
min(leftMax[i], rightMax[i]) - height[i] - 如果结果为正,则累加到总雨水量中
- 每个位置能接的雨水量 =
图解动态规划
输入数组:
height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
步骤1:计算 leftMax(从左到右)
leftMax[0] = height[0] = 0
leftMax[1] = max(leftMax[0], height[1]) = max(0, 1) = 1
leftMax[2] = max(leftMax[1], height[2]) = max(1, 0) = 1
leftMax[3] = max(leftMax[2], height[3]) = max(1, 2) = 2
...
leftMax = [0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3]
步骤2:计算 rightMax(从右到左)
rightMax[11] = height[11] = 1
rightMax[10] = max(rightMax[11], height[10]) = max(1, 2) = 2
rightMax[9] = max(rightMax[10], height[9]) = max(2, 1) = 2
rightMax[8] = max(rightMax[9], height[8]) = max(2, 2) = 2
...
rightMax = [3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1]
步骤3:计算每个位置的雨水量
雨水量[i] = min(leftMax[i], rightMax[i]) - height[i]
例如:
i=2: min(1, 3) - 0 = 1
i=4: min(2, 3) - 1 = 1
i=5: min(2, 3) - 0 = 2
...
总雨水量 = 1 + 1 + 2 + 1 + 1 = 6
🚀 代码实现
class Solution {
public int trap(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int n = height.length;
int[] leftMax = new int[n]; // 记录每个位置左侧的最大高度
int[] rightMax = new int[n]; // 记录每个位置右侧的最大高度
// 从左到右计算leftMax
leftMax[0] = height[0];
for (int i = 1; i < n; i++) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
// 从右到左计算rightMax
rightMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);
}
// 计算总雨水量
int totalWater = 0;
for (int i = 0; i < n; i++) {
totalWater += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return totalWater;
}
}
💡 复杂度分析
- 时间复杂度:O(n) → 遍历数组三次
- 空间复杂度:O(n) → 需要两个辅助数组
leftMax和rightMax
方法二:双指针法(优化空间复杂度)
关键思路:使用两个指针 left 和 right,动态维护左右最大高度,减少空间复杂度到O(1)。
图解双指针
初始状态:
height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
left = 0, right = 11
leftMax = 0, rightMax = 0
totalWater = 0
步骤1:height[left] < height[right]
left=0: height[0]=0 < height[11]=1
leftMax = max(0, 0) = 0
totalWater += 0 - 0 = 0
left++ → left=1
步骤2:height[left] < height[right]
left=1: height[1]=1 < height[11]=1
leftMax = max(0, 1) = 1
totalWater += 1 - 1 = 0
left++ → left=2
步骤3:height[left] < height[right]
left=2: height[2]=0 < height[11]=1
leftMax = 1
totalWater += 1 - 0 = 1
left++ → left=3
步骤4:height[left] < height[right]
left=3: height[3]=2 > height[11]=1
rightMax = max(0, 1) = 1
totalWater += 1 - 1 = 0
right-- → right=10
最终结果:
totalWater = 6
🚀 代码实现
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
int totalWater = 0;
while (left < right) {
// 更新左右最大高度
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
// 计算雨水量并移动指针
if (height[left] < height[right]) {
totalWater += leftMax - height[left];
left++;
} else {
totalWater += rightMax - height[right];
right--;
}
}
return totalWater;
}
}
💡 复杂度分析
- 时间复杂度:O(n) → 只需遍历数组一次
- 空间复杂度:O(1) → 仅使用常数空间
🌟 总结要点
✅ 动态规划核心思想:预处理每个位置的左右最大高度
✅ 双指针优化:空间复杂度降至O(1),适合大数据量
✅ 适用场景:区间最值问题、累积问题

287

被折叠的 条评论
为什么被折叠?



