四种解法
一、左右指针对撞
思路:左右比较取小值,将lower与记录的水位线进行比较,如果水位线高于lower,则水位线设为level=lower,然后记录该处可存水量water+=level-lower。这里相当于,时刻记录左右两边的最小值作为水位线,然后用水位线减去本处高度作为存水量。
class Solution { public: int trap(vector<int>& height) { int n=height.size(); int l=0,r=n-1,lower=0,level=0; int water=0; while(l<r) { lower=min(height[l],height[r]); if(height[l]<height[r]) l++; else r--; level=max(lower,level); water+=level-lower; } return water; } };
二、按照列求
思路
对每一列来说,都判断自己这一列能不能装水,如何判断呢?
看自己左边最高的墙(不包括自己)和右边最高的墙(不包括自己),根据短板效应取这俩中更小的那个值lowWall
lowWall比自己高那才可能出现凹坑才能存水对吧,旁边的比自己矮那不流出去了
然后计算这一列上存了多少水
首先两端的墙不用考虑,存不了,直接从第二个看
比如下面红色这一列能存几个水呢?
1.红色自己高度是1,左边最高是绿色,高度为0,右边最高是紫色,值为3,根据短板效应取lowWall = 0,
2.lowWall没有自己高,装不了
来看个能装的例子,下图我们来看红色框里这一列,它能装多少水呢?
首先,左边最高的墙是绿色,高度为2,右边最高的墙为紫色,高度为3,lowWall取值为2
lowWall大于红色自身高度1所以能装啊
那每列能装多少水呢?根据上图看出来,是(lowWall-自身高度)
最后按照这个方法计算每一列就完事了
逐句注释的代码实现
public int trap(int[] height) { int sum = 0; //最两端的列不用考虑,因为一定不会有水。所以下标从 1 到 length - 2 for (int i = 1; i < height.length - 1; i++) { int max_left = 0; //找出左边最高 for (int j = i - 1; j >= 0; j--) { if (height[j] > max_left) { max_left = height[j]; } } int max_right = 0; //找出右边最高 for (int j = i + 1; j < height.length; j++) { if (height[j] > max_right) { max_right = height[j]; } } //找出两端较小的 int min = Math.min(max_left, max_right); //只有较小的一段大于当前列的高度才会有水,其他情况不会有水 if (min > height[i]) { sum = sum + (min - height[i]); } } return sum; }
时间复杂度:O(n²),遍历每一列需要 nn,找出左边最高和右边最高的墙加起来刚好又是一个 n,所以是 n²。
空间复杂度:O(1)
三、动态规划(优化刚才的按列)
思路
刚才代码中出现了一些重复出现的循环遍历,每次计算新的一列时候,我们都要重新找两边最高的墙,完全没必要啊,利用动态规划的思想,把这些信息放到一个数组里就ok,动态规划的思想很复杂,在这里说不清,以后再详细讲解,反正大概就是把重复的方法归纳起来复用
逐句注释的代码实现
public int trap(int[] height) { //sum计算总和 // 新建两个长度和height一样的数组用来存 每一列的左右最高墙在height中的索引 int sum = 0; int[] max_left = new int[height.length]; int[] max_right = new int[height.length]; //求所有列的左边最高墙 索引都放在max_left里 for (int i = 1; i < height.length - 1; i++) { max_left[i] = Math.max(max_left[i - 1], height[i - 1]); } //求所有列的右边最高墙 索引都放在max_right里 for (int i = height.length - 2; i >= 0; i--) { max_right[i] = Math.max(max_right[i + 1], height[i + 1]); } //计算每一列的能存多少水累加起来 这一步和上面方法没区别 for (int i = 1; i < height.length - 1; i++) { int min = Math.min(max_left[i], max_right[i]); if (min > height[i]) { sum = sum + (min - height[i]); } } return sum; }
动态规划优化了求一侧最大值的操作,其余代码没变化
时间复杂度:O(n)
空间复杂度:O(n),用来保存每一列左边最高的墙和右边最高的墙。
相比刚才的方法,时间复杂度降了下来,空间复杂度略有上升
四、双指针(把刚才上升的空间复杂度给降下来成为指标最优的解法)
思路
这道题中,可以看到,max_left[ i ] 和 max_right[ i ] 数组中的元素我们其实只用一次,然后就再也不会用到了。所以我们可以不用数组,只用一个元素就行了。
逐句注释的代码实现
我们先改造下 max_left
public int trap(int[] height) { int sum = 0; int max_left = 0; //右侧的没改还用数组存 int[] max_right = new int[height.length]; for (int i = height.length - 2; i >= 0; i--) { max_right[i] = Math.max(max_right[i + 1], height[i + 1]); } //左侧的改了 for (int i = 1; i < height.length - 1; i++) { max_left = Math.max(max_left, height[i - 1]); int min = Math.min(max_left, max_right[i]); if (min > height[i]) { sum = sum + (min - height[i]); } } return sum; }
时间复杂度: O(n)
空间复杂度:O(1)
五、栈
思路
这道题好像就是想考栈= =
就是把墙的高度压入栈中,栈顶元素就是上一个墙的高度,如果指针指的当前墙比上一个墙高,那就说明,能存水,没有上一个墙高,那就存不了水,把这个墙放进去
具体思路都写代码注释里面了,注释已经不能再细了
逐句注释的代码实现
public int trap(int[] height) { /** * 初始栈 * 初始总和 * 初始指针指向第一个元素 */ Stack<Integer> stack = new Stack<>(); int sum = 0; int currIndex = 0; /** * 最后用大循环包围 */ while (currIndex < height.length) { /** * 如果下一个比栈顶的大,那就进行一系列操作 */ while (!stack.empty() && height[currIndex] > height[stack.peek()]) { /** * 1.首先取出栈顶元素 是计算面积的底 * 2.然后刚才的底没用了,出栈,我们要他之前的那个墙(新的栈顶)的高度,和currIndex指针指向的墙比,哪个矮,哪个-底就等于计算面积的高 * (矮墙-底) X 底边宽 = 面积 * 3.底边宽怎么求? 底边宽 = 后面的墙-前面的墙-1 * 4.所以最终 sum = 面积 = (矮墙-底) X 底边宽 */ //1. int bottomHeight = height[stack.peek()]; //2. stack.pop(); //这里注意 要是出栈以后 栈空了那就跳出循环进行下一个height里的元素操作 if (stack.empty()) break; //go on int lowerWall = Math.min(height[currIndex], height[stack.peek()]); //3. int bottomWide = currIndex - stack.peek() - 1; //4. sum = sum + (lowerWall - bottomHeight) * bottomWide; } /** * 如果下一个比栈顶的小,那就直接入栈 */ stack.push(currIndex); /** * 遍历记得指针往后走 */ currIndex++; } return sum; } }
————————————————
版权声明:本文为CSDN博主「RookieZc」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41132565/article/details/109160614