题目
题目连接:https://leetcode.com/problems...
如果只是从数组的左边扫描到右边,不可能在这种单向扫描时知道:在当前扫描到的的地板上方能够装多少水。因为能装多少水依赖于当前地板的两边的地形,而单向扫描时无法知道当前地板右边的地形。因此我们需要从两边向中间扫描。
从这一题中能学习到一些扫描数据和下标移动的技巧。
题解
class Solution
{
public:
int trap(vector<int> &height)
{
if (height.size() <= 2)
{
return 0;
}
int l = 0, r = height.size() - 1, l_max = height[l], r_max = height[r], water = 0;
while (l < r)
{
if (height[l] < height[r])
{
l++;
if (height[l] > l_max)
{
l_max = height[l];
}
water += l_max - height[l];
}
else
{
r--;
if (height[r] > r_max)
{
r_max = height[r];
}
water += r_max - height[r];
}
}
return water;
}
};
时间复杂度显然为O(n),一趟双向扫描就可以得到结果。
代码解释
我们维护2个扫描游标:l
和r
。
为了知道l
的左边的地形,我们维护了一个变量l_max
,表示l
左边(包括l
)最高的墙有多高。同理,r_max
表示r
右边(包括r
)最高的墙有多高。
每次移动游标时,我们只移动l
和r
中处于较低地形的游标。
不妨假设我们移动的是l
:
- 如果
l
移动后的位置比l_max
要低,那么l
所在的地板上方必定能困住l_max-l
的水量,因为l
的左边有l_max
比它高,右边有r
比它高,且r
比l_max
高(这一点比较难想到,请回想我们每次只移动比较低的游标)。此时将这些被困住的水量l_max-l
记录到water
结果变量中。 - 如果
l
移动后的位置与l_max
相同或更高,那么l
所在的地板上方肯定不能困住水(因为l
的左边没有更高的墙了)。并且更新l_max
为l
所在的高度。
最后两个游标肯定会在整个数组的最高点相遇!(请再次回想我们每次只移动比较低的游标)
总结
扫描数据和下标移动的技巧
可以看出,“每次只移动l
和r
中处于较低地形的游标”是一个非常巧妙的选择,它为我们移动后的状态提供了一些保障。比如说l<r,因此移动l,我们可以确信,移动以后l的右边肯定有比l更高的墙(至少r就比l要高)。更进一步地,可以保证:此次保持不动的游标r
肯定是当前已扫描到的地形的最高点(用反证法可以证明)。l_max
也有着更一般的含义:它象征着游标l
已经扫描到的信息。r_max
同理。扫描的过程中,我们可以存储那些有用的信息,以便将来与新的扫描结果相比较/做计算。
计算问题
不像这一题和这一题,本题并不是一个搜索问题。本题要求我们根据给定的信息计算出某种结果,不妨称它为计算问题。
对于计算问题,算法的步骤一般是:
- 扫描输入数据,提取、存储有用信息。
- 根据扫描得到的信息来进行计算,得到结果。
以下几个因素会影响到扫描的次数和计算阶段的复杂度:
- 每趟扫描获取到的信息数量
- 信息的存储方式
- 存储之前如何处理信息
因此,理想的计算问题算法应该:
- 尽可能地在一趟扫描中收集更多信息
- 存储信息的数据结构应该适应将来的计算算法
- 在扫描的过程中就处理信息,减轻计算阶段的负担
搜索问题也需要扫描输入,上面的建议同样适用。
与积分之间的关联
这道题不禁让人联想到数学里的积分:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。