题目
分析
因为这道题今天面试字节跳动给问到了原题,顺便自己也复习一遍检测一下自己。
首先这道题最简单想法就是遍历每一列检查能否装水,问题就难在怎么找出来这个装水的规律。
装水就意味着两边得要和这一列有高度差,然后才能产生装水的空间,而且是两边较低的那边,这个不难理解,根据木桶原理。明白这个我们就能进行分析。
就拿上面的case来分析。首先需要明确的一点是第一列是肯定装不了水的,原因就是题中的设定是第一列最左边是没有边界,不能围起来,所以图中第一列虽然高度是0,但是装不了。类似的道理,最右边那列也不行。
接着我们来分析可行的列,就看能储水的吧,比如图中的第三列,第六列。第三列高度为0,我们从这个位置往左找最高的列,是第二列高度为1,往右找最高的列,是第八列,高度为3,但是根据木桶原理,两边较矮的那边才能产生影响,所以看左边,它根这一列产生的高度差为1,所以蓄水量为1,这也是正确的。
再来看图中第6列,左边最高第四列,高度为2,右边最高第八列,高度为3,较低者是2,跟第六列高度差为2,所以这一列蓄水为2。其他的地方也可以类似的分析。
下面我们来总结一般的情况,当所求列高度比左右所有列都高,或者说是数组中最高的那列,显然它是储蓄不了水。当该列至少等于两边的其中一边时,肯定也是储不了水。所以唯一的情况就是该列高度小于两边的高度,然后用两边较矮的那边减去这一列的高度就是这一列的蓄水量。下面来写代码
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0;
if (height.size() == 0) return 0;
for (int i = 1; i < height.size() - 1; ++i) {
int lefth = INT_MIN, righth = INT_MIN;
for (int j = i + 1; j < height.size(); ++j) {
if (height[j] > righth) righth = height[j];
}
for (int j = i - 1; j >= 0; --j) {
if (height[j] > lefth) lefth = height[j];
}
if (min(lefth, righth) > height[i]) ans += min(lefth, righth) - height[i];
}
return ans;
}
};
ok submit一发,不出意外很慢。但毕竟是一道hard,能够暴力过也还算可以。但是学习算法肯定不能止步于此,我相信面试官在面试的时候
看到这样的算法,肯定也是不满足的,肯定需要进行优化。
分析一下,上面的算法慢主要是因为我们对每一列都需要遍历数组来找两边最高的列,这需要平方的时间复杂度。优化时间的基本原则就是空间换时间,所以容易想到的就是把每一列两边的最高列高度记录下来,类似哈希的道理。下面来实现这一点
不妨用两个数组分别记录下标位置的列两边的最高列高度,比如rightmax[i]和leftmax[i]数组就是分别存储第i列右边和左边最高列的高度。
那怎么来求这个数组呢,其实也很简单,根据i-1来递推即可,来看代码
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0;
if (height.empty()) return ans;
vector<int>leftmax(height.size()), rightmax(height.size());
// left to right
leftmax[0] = height.front();
for (int i = 1; i < height.size(); ++i) {
leftmax[i] = leftmax[i - 1] > height[i] ? leftmax[i - 1] : height[i];
}
// right to left
rightmax[height.size() - 1] = height.back();
for (int i = height.size() - 2; i >= 0; --i) {
rightmax[i] = rightmax[i + 1] > height[i] ? rightmax[i + 1] : height[i];
}
for (int i = 0; i < height.size(); ++i) {
ans += min(leftmax[i], rightmax[i]) > height[i] ? min(leftmax[i], rightmax[i]) - height[i] : 0;
}
return ans;
}
};
从左到右求左边最高列,从右往左求右边最高列,第一个位置左边最高列就是本身,最后一个位置右边最高列就是最后一个元素本身。
至于递推关系很简单,就不多啰嗦了。
submit一发,不出意外,时间复杂度从平方级别降低到了线性,所以效果也是很明显。
复习一下这道题,发现还没忘hhh,期待几天后的字节面试,希望能有好的结果。