题目:一个数组代表一组相邻的柱子的高度,柱子的宽度都是1,求下雨后积水的面积。
多个柱子上的积水可能连成片,当成一块考虑比较复杂,思路不往复杂的方向走。还是考虑单独计算每一个柱子上的积水面积。从循环方便的角度也指向这个方向。
一个柱子上的积水高度由左边最高和右边最高的小者决定,即min(max_left, max_right) - current。有点像可以买卖2次的股票问题。
最直观的算法:遍历柱子,对于每个柱子分别计算左边的最高,和右边的最高,然后计算面积。如果是从左往右遍历,则左边的最高用一个变量维护就好,是O(1)的,右边的最高则需要一个O(n)的遍历,总得复杂度就是O(n*n)。
为了寻求O(n)的算法,
1)先从右往左遍历一遍,得到每个柱子右边最高的柱子高度,用一个数组保存起来。
2)再从左往右遍历,计算每个柱子左边最高的同时,结合第一步的数据,计算柱子上的积水面积并累加。
int trap(int A[], int n) {
if (n <= 2) return 0;
vector<int> max_right(n, 0);
for(int i = n - 2; i >= 0; --i)
max_right[i] = max(max_right[i + 1], A[i + 1]);
int max_left = 0, sum = 0;
for (int i = 1; i < n; ++i) {
max_left = max(max_left, A[i - 1]);
int height = min(max_left, max_right[i]);
if (height > A[i])
sum += height - A[i];
}
return sum;
}
最近发现的方法,双指针夹逼法,只需O(1)空间
int trap(vector<int>& height) {
if (height.size() <= 2) return 0;
int l = 0, r = height.size() - 1, lMax = 0, rMax = 0, area = 0;
for (; l < r;) {
lMax = max(lMax, height[l]);
rMax = max(rMax, height[r]);
if (lMax < rMax) {
area += lMax - height[l];
++l;
}
else {
area += rMax - height[r];
--r;
}
}
return area;
}