内容介绍
给定
n
个非负整数表示每个宽度为1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。示例 2:
输入:height = [4,2,0,3,2,5] 输出:9提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
完整代码
int trap(int* height, int heightSize) {
int n = heightSize;
if (n == 0) {
return 0;
}
int leftMax[n];
memset(leftMax, 0, sizeof(leftMax));
leftMax[0] = height[0];
for (int i = 1; i < n; ++i) {
leftMax[i] = fmax(leftMax[i - 1], height[i]);
}
int rightMax[n];
memset(rightMax, 0, sizeof(rightMax));
rightMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; --i) {
rightMax[i] = fmax(rightMax[i + 1], height[i]);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += fmin(leftMax[i], rightMax[i]) - height[i];
}
return ans;
}
思路详解
解题思路
这个问题可以通过计算每个柱子上方可以积累的水量来解决。对于每个柱子,它上方可以积累的水量取决于它左边最高的柱子和右边最高的柱子中的较小者。
步骤分解
-
初始化辅助数组
leftMax
:一个数组,用来存储从左到右每个位置左侧的最高柱子高度。rightMax
:一个数组,用来存储从右到左每个位置右侧的最高柱子高度。
-
填充
leftMax
数组- 遍历
height
数组,对于每个位置i
,leftMax[i]
应该是leftMax[i-1]
和height[i]
中的较大者。这意味着leftMax[i]
存储了位置i
左侧(包括i
)最高的柱子高度。
- 遍历
-
填充
rightMax
数组- 从右向左遍历
height
数组,对于每个位置i
,rightMax[i]
应该是rightMax[i+1]
和height[i]
中的较大者。这意味着rightMax[i]
存储了位置i
右侧(包括i
)最高的柱子高度。
- 从右向左遍历
-
计算水量
- 对于每个位置
i
,它能积累的水量等于min(leftMax[i], rightMax[i]) - height[i]
。这是因为水会被左侧和右侧最高柱子中的较小者限制。 - 将每个位置的水量累加,得到总的积水量。
- 对于每个位置
代码详解
int trap(int* height, int heightSize) {
int n = heightSize;
if (n == 0) {
return 0; // 如果数组为空,则无法积水,直接返回0。
}
int leftMax[n];
memset(leftMax, 0, sizeof(leftMax)); // 初始化leftMax数组。
leftMax[0] = height[0]; // 第一个元素左侧没有更高的柱子,所以leftMax[0]等于height[0]。
for (int i = 1; i < n; ++i) {
leftMax[i] = fmax(leftMax[i - 1], height[i]); // 更新leftMax数组。
}
int rightMax[n];
memset(rightMax, 0, sizeof(rightMax)); // 初始化rightMax数组。
rightMax[n - 1] = height[n - 1]; // 最后一个元素右侧没有更高的柱子,所以rightMax[n-1]等于height[n-1]。
for (int i = n - 2; i >= 0; --i) {
rightMax[i] = fmax(rightMax[i + 1], height[i]); // 更新rightMax数组。
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += fmin(leftMax[i], rightMax[i]) - height[i]; // 计算每个位置的水量并累加。
}
return ans; // 返回总的积水量。
}
总结
该算法首先通过两次遍历构建了两个辅助数组 leftMax
和 rightMax
,然后在第三次遍历中计算并累加每个位置的水量。最终得到整个直方图容器可以积累的水量。这个算法的时间复杂度为 O(n),空间复杂度也为 O(n)。
知识点精炼
辅助数组的应用
- 使用两个辅助数组
leftMax
和rightMax
来存储每个位置左侧和右侧的最大高度。
2. 动态规划思想
- 通过遍历直方图数组,动态更新
leftMax
和rightMax
,体现了动态规划中“状态转移”的概念。
3. 边界条件处理
- 对于
leftMax
的第一个元素和rightMax
的最后一个元素,它们没有左侧或右侧的元素,因此直接赋值为对应的高度。
4. 数组初始化
- 使用
memset
函数初始化辅助数组,确保数组的初始状态是已知的。
5. 最大值与最小值计算
- 使用
fmax
和fmin
函数来计算两个值中的最大值和最小值,分别用于更新辅助数组和计算水量。
6. 累加求和
- 通过一个循环遍历直方图数组,计算每个位置可以积累的水量并累加到总水量中。
7. 空间复杂度与时间复杂度
- 算法的时间复杂度为 O(n),因为涉及三个独立的遍历操作。
- 空间复杂度也为 O(n),由于使用了两个额外的数组来存储最大高度信息。
8. 特殊情况处理
- 在数组为空时,直接返回 0,处理了边界情况。