接雨水解法详解:

题目:

基本思路:从图上可以看出要想接住雨水,必须是凹字形的,也就是当前位置的左右两边必须存在高度大于它的地方,所以我们要想知道当前位置最多能存储多少水,只需找到左边最高处max_left和右边最高处max_right,取他们两个较小的那边计算即可(短板效应)。

其实接下来的解法要解决的问题就是如何找到max_rightmax_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_leftmax_right,显而易见是可以的,所以我们需要声明两个数组max_leftmax_right来存储每个位置对应的左右最高点,因为我们只在它左右两边寻找,我们可以写出当前位置imax_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.接雨水)多解法详解的更多相关文章

  1. leetcode#42 Trapping rain water的五种解法详解

    leetcode#42 Trapping rain water 这道题十分有意思,可以用很多方法做出来,每种方法的思想都值得让人细细体会. 42. Trapping Rain WaterGiven n ...

  2. Java实现 LeetCode 42 接雨水

    42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这 ...

  3. [LeetCode]42. 接雨水(双指针,DP)

    题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下, ...

  4. leetcode 42. 接雨水 JAVA

    题目: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下 ...

  5. HDU 4622 Reincarnation Hash解法详解

    今天想学字符串hash是怎么弄的.就看到了这题模板题 http://acm.hdu.edu.cn/showproblem.php?pid=4622 刚开始当然不懂啦,然后就上网搜解法.很多都是什么后缀 ...

  6. Leetcode 42.接雨水

    接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下 ...

  7. Leetcode 42 接雨水 双指针

    地址 https://leetcode-cn.com/problems/trapping-rain-water/ 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能 ...

  8. LeetCode 42. 接雨水(Trapping Rain Water)

    题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况 ...

  9. C++旋转数组(三种解法详解)

    题目描述 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 附加要求 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题. 你可以使用空间复杂度为 O(1) 的 原地 ...

随机推荐

  1. 简单易懂的Servlet路径问题

    关于servlet路径,我看了一下网上别人的博客园,发现都有一个通病,讲的太专业了,又抓不住关键部分,往往看一眼就不想看第二眼.所以我特地准备了初学者所通识的servlet路径问题. 1.标识符 /j ...

  2. 每日一点:git 与 github 区别

    絮絮叨叨在前:以前的公司,都用svn 进行代码管理.最近我那程序猿先生真的受不了我,强迫我使用tortoiseGit. 一开始对于 git 和 github 傻傻分不清,干脆自己整理资料,总结一下. ...

  3. 安装docker,docker-compose,Harbor

    一.docker安装 1.删除旧版本和相关依赖 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ d ...

  4. js之重写原型对象

    “实例中的指针仅指向原型,而不是指向构造函数”. “重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的关系:它们引用的仍然是最初的原型”.——前记 var fun = function(){ ...

  5. 小程序的数据存储,与Django等服务发送请求

    目录 官方文档 快速归纳 存取改删 1.wx存储数据到本地以及本地获取数 1.1 wx.setStorageSync(string key, any data) 存(同步) 1.2 wx.setSto ...

  6. 实验二 Samba服务器配置

    实验二 实 验 基 本 信 息 实验名称:Samba服务器配置 实验时间:    年 月 日 实验地点: 实验目的: 了解Samba环境及协议 掌握Samba的工作原理 掌握主配置文件Samba.co ...

  7. scrapy的扩展件extensions

    extensions.py文件 # -*- coding: utf-8 -*- # 该扩展会在以下事件时记录一条日志: # spider被打开 # spider被关闭 # 爬取了特定数量的条目(ite ...

  8. Linux启动nginx时报错nginx: [emerg] getpwnam("nginx") failed

    编译时指定了用户而没有创建用户导致报错 解决: 查看你添加的用户是什么, [root@localhost nginx]# sbin/nginx -Vnginx version: nginx/1.10. ...

  9. 利用mnist数据集进行深度神经网络

    初始神经网络 这里要解决的问题是,将手写数字的灰度图像(28 像素 x28 像素)划分到 10 个类别中(0~9).我们将使用 MINST 数据集,它是机器学习领域的一个经典数据集,其历史几乎和这个领 ...

  10. 2020年IOS超级签最新实现原理详解

    相信2019年最火的应该就是这个东西了,我也是摸着石头过河,勉强混进了这个行业! 超级签这个东西吧,说白了就是用个人账号分发应用,大致分成以下几个步骤吧 一.使用配置文件获取UDID 苹果公司允许开发 ...