LeetCode(42.接雨水)多解法详解
接雨水解法详解:
题目:

基本思路:从图上可以看出要想接住雨水,必须是凹字形的,也就是当前位置的左右两边必须存在高度大于它的地方,所以我们要想知道当前位置最多能存储多少水,只需找到左边最高处max_left和右边最高处max_right,取他们两个较小的那边计算即可(短板效应)。
其实接下来的解法要解决的问题就是如何找到max_right和max_left。
不过我们首先来看一个无法AC的解法:
解法一:按行
按行顾名思义就是一行一行地进行计算,首先我们计算第一行,设置一个变量temp临时存储当前接的水和开始标志isStart,当碰到第一个高度大于等于行数的位置时,temp置0,开始计算,接下来如果碰到小于行数的位置时,temp+1,碰到大于行数的位置时,temp置0,继续往后遍历,如此遍历完一行继续遍历下一行。

class Solution {
public int trap(int[] height) {
int sum = 0;
int max = getMax(height);//找到最大的高度,以便遍历。
for (int i = 1; i <= max; i++) {
boolean isStart = false; //标记是否开始更新 temp
int temp_sum = 0;
for (int j = 0; j < height.length; j++) {
if (isStart && height[j] < i) {
temp_sum++;
}
if (height[j] >= i) {
sum = sum + temp_sum;
temp_sum = 0;
isStart = true;
}
}
}
return sum;
}
private int getMax(int[] height) {
int max = 0;
for (int i = 0; i < height.length; i++) {
if (height[i] > max) {
max = height[i];
}
}
return max;
}
}
不过此解法会在最后两个测试用例处,TLE掉,我们只需了解一下这种思想,medium难度的题还是可以过的。
解法二:按列
这个解法就是我们刚开始所说思路的最朴素的解法了,要求i位置接水量,只需找到i左边最高位置的高度max_left和右边最高位置的高度max_right,然后取较小的那个min_height=min(max_right,max_left),最后计算i位置接水量:min_height-height[i],当然如果min_height<height[i],就不用计算啦。

class Solution {
public int trap(int[] height) {
int ans=0;
for(int i=1;i<height.length-1;i++){//第一个和最后一个位置肯定存不了水
int max_left=0;
for(int j=i-1;j>=0;j--){//找到当前位置左边最高处
max_left=Math.max(max_left,height[j]);
}
int max_right=0;
for(int j=i+1;j<height.length;j++)//找到当前位置右边最高处
max_right=Math.max(max_right,height[j]);
int min_high=Math.min(max_right,max_left);
if(min_high>height[i]){
ans+=min_high-height[i];
}
}
return ans;
}
}
解法三:动态规划
我们知道解法二每到一个位置都会遍历左右两边来寻找它的左右最高处,这样就会导致O(n^2)的时间复杂度,我们能不能事先就找好每个位置对应的max_left和max_right,显而易见是可以的,所以我们需要声明两个数组max_left和max_right来存储每个位置对应的左右最高点,因为我们只在它左右两边寻找,我们可以写出当前位置i的max_left[i]=max(max_left[i-1],height[i-1]),右边同理,接下来就是和解法二一样了。
class Solution {
public int trap(int[] height) {
int[] max_left=new int[height.length];
int[] max_right=new int[height.length];
int ans=0;
for(int i=1;i<height.length-1;i++){
max_left[i]=Math.max(max_left[i-1],height[i-1]);
}
for(int i=height.length-2;i>=1;i--){
max_right[i]=Math.max(max_right[i+1],height[i+1]);
}
for(int i=1;i<height.length-1;i++){
int min_height=Math.min(max_right[i],max_left[i]);
if(min_height>height[i]){
ans+=min_height-height[i];
}
}
return ans;
}
}
解法四:双指针
从动态规划解法可以看出,只要max_left[i]<max_right[i],积水的高度由max_left[i]决定,也就是积水的高度是由较低的那边决定,所以此时我们应该继续由较低->较高那个方向遍历,直到此时较低的这边发现比另一边更高的位置,再转换方向,这样我们就可以一次遍历且只需两个指针便可完成计算。

- 初始化 left 指针为 0 并且 right 指针为 size-1
While left<right, do:
If height[left] < height[right]
If height[left]≥left_max, 更新 left_max
Else left_max−height[left] 到ans
left = left + 1.Else
If height[right]≥right_max, 更新 right_max
Else 累加 right_max−height[right] 到 ans
right = right - 1.
int trap(vector<int>& height)
{
int left = 0, right = height.size() - 1;
int ans = 0;
int left_max = 0, right_max = 0;
while (left < right) {
if (height[left] < height[right]) {
height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
++left;
}
else {
height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
--right;
}
}
return ans;
}
解法五:单调栈
思路:单调栈可以保证栈底到栈顶是单调递减的,也就是说栈顶元素可以由它前一个元素所界定,我们遍历数组时可以维护一个单调栈来存储索引,当当前元素小于栈顶元素时,入栈,当当前元素大于等于栈顶元素时,此时栈顶元素的接水量就取决于其前一个元素和当前元素较小的那个,栈顶元素出栈。
算法:
- 使用栈来存储条形块的索引下标。
- 遍历数组:
- 当栈非空且 height[current]>height[st.top()]
- 意味着栈中元素可以被弹出。弹出栈顶元素
- 计算当前元素和栈顶元素的距离,准备进行填充操作
distance=current−st.top()−1
- 找出界定高度
- bounded_height=min(height[current],height[st.top()])−height[top]
- 往答案中累加积水量ans+=distance×bounded_height
- 将当前索引下标入栈
- 将 current 移动到下个位置
int trap(vector<int>& height)
{
int ans = 0, current = 0;
stack<int> st;
while (current < height.size()) {
while (!st.empty() && height[current] > height[st.top()]) {
int top = st.top();
st.pop();
if (st.empty())
break;
int distance = current - st.top() - 1;
int bounded_height = min(height[current], height[st.top()]) - height[top];
ans += distance * bounded_height;
}
st.push(current++);
}
return ans;
}
总结:
这种多解法的题目可以尽量去了解它的每一种解法,这对扩展自己的解题思维有很大的帮助,其实这个题目这么多解法大部分都是在解决怎么高效找出左右两边最高点,时间复杂度由O(n^2)提升到O(n),空间复杂度也由O(n)->O(1),所以有时候很多方法都是由暴力逐渐改善的,本人表达可能词不达意,还请谅解,如有任何问题,请留言指出,不甚感激。
参考出处
LeetCode(42.接雨水)多解法详解的更多相关文章
- leetcode#42 Trapping rain water的五种解法详解
leetcode#42 Trapping rain water 这道题十分有意思,可以用很多方法做出来,每种方法的思想都值得让人细细体会. 42. Trapping Rain WaterGiven n ...
- Java实现 LeetCode 42 接雨水
42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这 ...
- [LeetCode]42. 接雨水(双指针,DP)
题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下, ...
- leetcode 42. 接雨水 JAVA
题目: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下 ...
- HDU 4622 Reincarnation Hash解法详解
今天想学字符串hash是怎么弄的.就看到了这题模板题 http://acm.hdu.edu.cn/showproblem.php?pid=4622 刚开始当然不懂啦,然后就上网搜解法.很多都是什么后缀 ...
- Leetcode 42.接雨水
接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下 ...
- Leetcode 42 接雨水 双指针
地址 https://leetcode-cn.com/problems/trapping-rain-water/ 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能 ...
- LeetCode 42. 接雨水(Trapping Rain Water)
题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况 ...
- C++旋转数组(三种解法详解)
题目描述 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 附加要求 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题. 你可以使用空间复杂度为 O(1) 的 原地 ...
随机推荐
- PYTHON程序设计实验
Python程序设计实验报告一: 熟悉IDLE和在线编程平台 安徽工程大学 Python程序设计实验报告 班级 物流191 姓名 邹缕学号 3190505117成绩 ▁▁▁ 日期 2020.3.5 指 ...
- linux yum安装MySQL5.6
1.新开的云服务器,需要检测系统是否自带安装mysql # yum list installed | grep mysql 2.如果发现有系统自带mysql,果断这么干 # yum -y remove ...
- oracle根据特定字符拆分字符串的方法
清洗数据需要将某个字段内以空格分隔的字符串拆分成多行单个的字符串,百度了很多种方法大概归结起来也就这几种方法最为有效,现在把贴出来: 第一种: select regexp_substr('1 2 3' ...
- CyclicBarrier源码探究 (JDK 1.8)
CyclicBarrier也叫回环栅栏,能够实现让一组线程运行到栅栏处并阻塞,等到所有线程都到达栅栏时再一起执行的功能."回环"意味着CyclicBarrier可以多次重复使用,相 ...
- MATLAB神经网络(3) 遗传算法优化BP神经网络——非线性函数拟合
3.1 案例背景 遗传算法(Genetic Algorithms)是一种模拟自然界遗传机制和生物进化论而形成的一种并行随机搜索最优化方法. 其基本要素包括:染色体编码方法.适应度函数.遗传操作和运行参 ...
- 使用 Github Action 进行前端自动化发布
前言 说起自动化,无论是在公司还是我们个人的项目中,都会用到或者编写一些工具来帮助我们去处理琐碎重复的工作,以节约时间提升效率,尤其是我们做前端开发会涉及诸如构建.部署.单元测试等这些开发工作流中重复 ...
- vue后台管理系统权限处理
vue后台管理系统权限 1.权限问题:用户和管理员进入管理系统看到的模块是不一样的,管理员看的的要比用户看到的多.需要用到动态加载路由,router.addRouters()来动态的挂载路由 // 1 ...
- Web的服务器和Javaweb结构
上一节介绍了Eclipse中集成Tomcat环境搭建及javaweb项目的创建,下面说说什么是web服务器及javaweb的结构. 1.web应用的演变 1.1 b/s与c/s模式 B/S:Brows ...
- LoadRunner录制HTTPS协议脚本
学习LoadRunner录制HTTPS协议脚本,其实是一个意外的收获.当我拿到要测试的URL时,我像以前的步骤一样录制脚本,但是录制结束后,发现并没有生成脚本,开始以为是LoadRunner的原因,我 ...
- [Docker7]Harbor
harbor download harbor offline tar package wget https://github.com/vmware/harbor/releases/download/v ...