当前位置: 首页 > news >正文

(leetcode) 力扣100 7.接雨水(两种非官解,三种官解,对官解进一步解释)

题目

在这里插入图片描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
在这里插入图片描述

数据范围

n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105

样例

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

代码

博主的垃圾代码,是在没考虑,动态规划,单调栈,以及双指针优化的情况下写的 O(n2),博主引以为鉴,不动脑的后果,大家不必观看。

package leetcode100;/*** @author akuya* @create 2025-05-07-9:09*/
public class seven {public static void main(String[] args) {int res= trap(new int[]{4,2,3});System.out.println(res);}public static int trap(int[] height) {int len=height.length;int a[]= height.clone();//前缀和for(int i=1;i<len;i++){a[i]=a[i]+a[i-1];}int res=0;/*  遍历for(int i=0;i<len-2;i++){int top=-1;int flag=0;for(int j=i+1;j<len;j++){if(top==-1||height[j]>height[top]){top=j;}if(height[i]<=height[j]){res+=(Math.min(height[i],height[j])*(j-i-1)-(a[j-1]-a[i]));i=j-1;flag=1;break;}}if(flag==0){res+=Math.min(height[i],height[top])*(top-i-1)-(a[top-1]-a[i]);i=top-1;}}*/int first=0,second=1;int top=1;while (first<=len-2&&second<len){//记录寻找过程中的最高点,以免所有后续点都比当前点低if(height[second]>=height[top]){top=second;}//寻找first点右侧的第一个高于first的点secondif(height[second]>=height[first]){res+=height[first]*(second-first-1)-(a[second-1]-a[first]);first=second;top=first+1;}second++;//如果没找到,则用最高点代替if(second==len){if(first<len-2&&first<top){res+= Math.min(height[first],height[top])*(top-first-1)-(a[top-1]-a[first]);first=top;second=first+1;top=second;}}}return res;}
}

大佬解法O(n)思路清晰新颖,与官方的双指针解法有异曲同工之妙

class Solution {
public:int trap(vector<int>& height) {int maxHeight = 0;int maxHeightPos = -1;for (int i=0;i<height.size();i++) {if (height[i] > maxHeight) {maxHeight = height[i];maxHeightPos = i;}}if (maxHeightPos == -1) return 0;int waterHeight = 0;int waterSum = 0;for (int i=0;i<maxHeightPos;i++) {// 左侧水位if (height[i] > waterHeight) waterHeight = height[i];waterSum += waterHeight - height[i];}waterHeight = 0;for (int i=height.size()-1;i>maxHeightPos;i--) {// 右侧水位if (height[i] > waterHeight) waterHeight = height[i];waterSum += waterHeight - height[i];}return waterSum;}
};

官方解法1 动态规划,思路最简单,感觉结合时间与思考成本个人认为最优解法

class Solution {public int trap(int[] height) {int n = height.length;if (n == 0) {return 0;}int[] leftMax = new int[n];leftMax[0] = height[0];for (int i = 1; i < n; ++i) {leftMax[i] = Math.max(leftMax[i - 1], height[i]);}int[] rightMax = new int[n];rightMax[n - 1] = height[n - 1];for (int i = n - 2; i >= 0; --i) {rightMax[i] = Math.max(rightMax[i + 1], height[i]);}int ans = 0;for (int i = 0; i < n; ++i) {ans += Math.min(leftMax[i], rightMax[i]) - height[i];}return ans;}
}作者:力扣官方题解
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/692342/jie-yu-shui-by-leetcode-solution-tuvc/
来源:力扣(LeetCode

官方解法二,单调栈,时空复杂度与解法一相近,但思维难度高一点。

class Solution {public int trap(int[] height) {int ans = 0;Deque<Integer> stack = new LinkedList<Integer>();int n = height.length;for (int i = 0; i < n; ++i) {while (!stack.isEmpty() && height[i] > height[stack.peek()]) {int top = stack.pop();if (stack.isEmpty()) {break;}int left = stack.peek();int currWidth = i - left - 1;int currHeight = Math.min(height[left], height[i]) - height[top];ans += currWidth * currHeight;}stack.push(i);}return ans;}
}作者:力扣官方题解
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/692342/jie-yu-shui-by-leetcode-solution-tuvc/
来源:力扣(LeetCode

官方解法三,双指针,虽然时空复杂度达到了最优,但思维难度最高,很难想到,看官方题解都需要理解一段时间(大佬除外)

class Solution {public int trap(int[] height) {int ans = 0;int left = 0, right = height.length - 1;int leftMax = 0, rightMax = 0;while (left < right) {leftMax = Math.max(leftMax, height[left]);rightMax = Math.max(rightMax, height[right]);if (height[left] < height[right]) {ans += leftMax - height[left];++left;} else {ans += rightMax - height[right];--right;}}return ans;}
}作者:力扣官方题解
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/692342/jie-yu-shui-by-leetcode-solution-tuvc/
来源:力扣(LeetCode

思路

首先我们观察这道题,数据量并不高,就算是O(N2)时间复杂度也能勉强通过,再构思处理思路,发现,内层遍历绝大数情况都远小于数组长度,除极端情况外,实际时间复杂度会远小于O(N2)。所以博主给出的第一份代码,就是未经过任何优化,采用O(N2),遍历每个点右侧第一个高于自己的点,求这段距离的储水量。思路简单,想到找第一个高于自己的点也不算难,对于只想通过题目是不二之选。

但是,既然做leetcode题了,面试官怎么会管你实际复杂度小于O(N2),他们关注的就是你的方法最差情况的时间复杂度,所以不要学不爱动脑的博主,我们肯定是需要研究更优的方法的。

解法二
首先我想讲的思路是leetcode中某位路人大佬的思路,大佬的思路是先确定最高的柱子,那么在最高的柱子左侧,所有水柱,一定是从左到右递增的,为什么,如图
在这里插入图片描述

因为已经确定了最高柱,那么确定水位高度的永远是从做到右的次高柱,我们从左到右遍历,遍历中的临时最高柱的高度永远会比最高柱低,那么临时最高柱的高度就是实际水位高度,右侧比他小的柱子的高度并不能影响这片水位的高度,导致随着从左到右的遍历,水位高度永远随着遍历过程中的临时最高柱增长。右侧同理。

那么,我们在确定最高柱后,只需要分别从两侧遍历一次,记录临时最高柱,然后就能计算到总储水量。

思维很巧妙,但也有一定思维难度,大家可以借鉴学习。

解法三
解法三是我最推荐的,最符合大众的解法,因为他思路简单,时空复杂度也满足要求。这个解法的着眼点在于优化遍历。我们普通遍历时间复杂度回到O(N2)级别,如何优化,可以用动态规划的思想。

动态规划思想的基础很简单,自底向上,通过底部的结果方便顶部结果的计算,从而达到减少时间复杂度,减少重复计算的目的。这道题搞好满足,我们都知道,假如每个点左侧的最大高度l,右侧的最大高度r,那么该点的储水量就是Math.min(l,r)-heigh[i]。而每个点的左侧最大高度l,又可以拆分为,math(height[i-1],与l[i-1])。这就是我们动态规划的状态转移方程,依照这个思路,就能以O(N)的时间复杂度完成计算每个点左侧的最大高度。右侧同理。

之后的遍历计算就不用我说明了。

解法四
单调栈的思路也同样不难,但与之前的思路都有点不一样,不常用单调栈或者不熟悉的朋友可能想不到单调栈,我们从左到右维持递减的单调栈。如果此时遍历到了i,i的高度大于i-1,由于单调栈我们知道,i-2的高度也是大于i-1的,那么当前遍历进度下,i-1这个点的储水量我们就能计算出了(注注意,计算水位高度要减去i-1的高度,因为这种方法计算的并不是这个点最终水位高度,i-1高度的水之前就可能计算过,如图),然后将计算过的点弹出,同时根据新进入的点i维持单调栈,遍历一遍即可。

在这里插入图片描述
解法五
思路最难的一种解法,如果让博主讲清楚的话,表达能力差的博主可能要写很多很多(很有自知之明),博主现在这里主要是对官方讲解的解释,因为官网讲的也很模糊,博主的讲解只是为了方便大家理解官方的讲解。

官方的意思是,维持左右指针,就能替换动态规划中的数组,从而节约空间,为什么能替换,只字未提。博主主要讲解这里。

首先应用官方对双指针操作的描述

维护两个指针 left 和 right,以及两个变量 leftMax 和 rightMax,初始时
left=0,right=n−1,leftMax=0,rightMax=0。指针 left 只会向右移动,指针 right
只会向左移动,在移动指针的过程中维护两个变量 leftMax 和 rightMax 的值。

当两个指针没有相遇时,进行如下操作:

使用 height[left] 和 height[right] 的值更新 leftMax 和 rightMax 的值;

如果 height[left]<height[right],则必有 leftMax<rightMax,下标 left 处能接的雨水量等于
leftMax−height[left],将下标 left 处能接的雨水量加到能接的雨水总量,然后将 left 加 1(即向右移动一位);

如果 height[left]≥height[right],则必有 leftMax≥rightMax,下标 right 处能接的雨水量等于
rightMax−height[right],将下标 right 处能接的雨水量加到能接的雨水总量,然后将 right 减
1(即向左移动一位)。

当两个指针相遇时,即可得到能接的雨水总量。

作者:力扣官方题解
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/692342/jie-yu-shui-by-leetcode-solution-tuvc/
来源:力扣(LeetCode)

首先为了大家理解,大家可以先看对解法二的讲解,借用解法二的思路,当height[left]<height[right],也就是右侧点高度大于左侧点高度时,从左遍历,遍历点的储水量就是左侧临时最高点-hight[i](解法二中有讲,虽然right不是最高柱,左右指针两个点的角度来看时,右指针高度大,他就是最高柱,这也是为什么左指针小于右指针时,我们处理左边而不是右边)。其他情况同理。

简单来说左右指针,哪边高,就将哪边当为最高柱,再根据我解法二的思路,就能求得该点的储水量。十分巧妙,实际算法是与解法二如出一辙的,只是解法二的思路更容易想到,实现也更简单。

这道题实在废了博主很多心思,其实昨天就再leetcode做出来了,奈何完全弄懂所有方法并且编写博客实在耗时,博主平时也有其他事,导致没能及时发出来。但就算如此,写博客是博主巩固自己知识的过程,博主不想草草了事,大家共勉。

相关文章:

  • QT实现曲线图缩放、拖拽以及框选放大
  • 【特别版】Kubernetes集群安装(1master,2node)
  • docker 安装 sqlserver2022 和注意点
  • 长事务:数据库中的“隐形炸弹“——金仓数据库运维避坑指南
  • P2415 集合求和 详解
  • 需求分析阶段测试工程师主要做哪些事情
  • Kubernetes探针生产环境实战指南
  • Linux下部署Keepalived
  • 代理服务器
  • DBeaver查询PostgreSQL的只读模式
  • vue实现半圆转盘旋转(门户网页上)
  • 基于Stable Diffusion XL模型进行文本生成图像的训练
  • 旧版 Flutter 写的项目, 想要在新的环境上运行?
  • ARM 芯片上移植 Ubuntu 操作系统详细步骤
  • 【HarmonyOS 5】鸿蒙中进度条的使用详解
  • HarmonyOS-hdc远程网络方式连接设备
  • 奥威BI:AI+BI深度融合,重塑智能AI数据分析新标杆
  • SpringBoot使用定时线程池ScheduledThreadPoolExecutor
  • Android MVC架构的现代化改造:构建清晰单向数据流
  • Rspack:字节跳动自研 Web 构建工具-基于 Rust打造高性能前端工具链
  • 中国天主教组织发贺电对新教皇当选表示祝贺
  • 李干杰走访各民主党派中央和全国工商联机关
  • 诺和诺德一季度减重版司美格鲁肽收入增83%,美国市场竞争激烈下调全年业绩预期
  • 央行:5月15日起下调金融机构存款准备金率0.5个百分点
  • 日本儿童人数已连续44年减少,少子化问题越发严重
  • 长三角铁路今日预计发送386万人次,沪宁、沪杭等方向尚有余票