LeetCode 42.接雨水

news2024/11/15 8:58:41

这篇记录一下刷接雨水这道题的过程,日后回顾

目录

法1:

法2:

法3:

法4:

法5:


务必掌握123

写这道题要知道雨水怎么算。核心就是要算当前列雨水的高度就要取决于这列左右两侧比自己搞得柱子中较矮的那一个柱子

当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。

你要能接水说明你得是个凹才可以,比如上面第一个雨水,那一列是空的左边高度是1,右边是2,取个最小的减去当列的高度,就是你当列能接多少水

那么第一种方法就出来了,暴力去找每一列的两侧的那个高柱子,就去计算雨水,然后把每次计算的雨水累加就行

法1:

class Solution {
public:
    int trap(vector<int>& height) {
        int sum = 0;
        for (int i = 0; i < height.size(); i++) {
            // 第一个柱子和最后一个柱子不接雨水
            if (i == 0 || i == height.size() - 1) continue;

            int rHeight = height[i]; // 记录右边柱子的最高高度
            int lHeight = height[i]; // 记录左边柱子的最高高度
            for (int r = i + 1; r < height.size(); r++) {
                if (height[r] > rHeight) rHeight = height[r];
            }
            for (int l = i - 1; l >= 0; l--) {
                if (height[l] > lHeight) lHeight = height[l];
            }
            //两边高柱子的小的减去当前列,计算雨水
            int h = min(lHeight, rHeight) - height[i];
            if (h > 0) sum += h;
        }
        return sum;
    }
};

这么写的话我试了,在力扣上跑不过去,时间复杂度是on2,因为要遍历每一列两边的最高柱子,相当于每走一次就要向两边去探测

 

法2:

下面解决方法就是降低复杂度了,因为思路是没问题的,下面这种方法可以复杂度降低到on,不空空间上又成了on,用用备忘录或者说就是动态规划

怎么去优化呢?

上面我们每一次都要去计算一下当前列左右两边的最高柱子是谁,有很多重复计算,这下我们可以定义两个数组去记录一下每次左边最高的柱子和右边最高的柱子是谁,像备忘录一样

比如

使用两个数组 maxleft 和 maxright,maxleft[i] 表示下标 i 左边最高柱子的高度,maxright[i] 表示下标 i 右边最高柱子的高度,这样的话一次遍历就可以了,代价就是空间复杂度高了

代码示例:

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()<=2)
        {
            return 0;
        }
        //记录每个柱子左边最高的柱子高度
        vector<int>ml(height.size());
        //同理右边
        vector<int>mr(height.size());
        int n=height.size();

        ml[0]=height[0];
        mr[n-1]=height[n-1];
        //存到备忘录里
        for(int i=1;i<n;++i){
            ml[i]=max(height[i],ml[i-1]);//看看当前的和左边最高的谁高,ml存的就是每次最高的,如果2最高连着存进去,那么ml[i-1],ml[i-2]..都是2,都是最高的,这个要理解
        }
        for(int i=n-2;i>=0;--i)
        {
            mr[i]=max(height[i],mr[i+1]);
        }

        //开始算雨水
        int sum=0;
        for(int i=0;i<n;++i){
            int tmp=min(ml[i],mr[i])-height[i];
            if(tmp>0){
                 sum+=tmp;
            }    
        }
        return sum;

    }
};

法3:

在时间On的基础上,降低空间,把上面备忘录方法里两个数组用指针去代替操作一下,这就是双指针法,很巧妙

这个题双指针的话思路都是一样,就是实现上面很巧妙,我也是搞了很久才搞清楚

拿代码来说:

class Solution {
public:
    int trap(vector<int>& height) {
        int res=0;
        int left=0,right=height.size()-1;
        int n = height.size();
        int l_max = height[0];
        int r_max = height[n - 1];
        while(left<right)//每次更新左右高度的最大值
        {
            l_max = max(l_max, height[left]);
             r_max = max(r_max, height[right]);
        
        // ans += min(l_max, r_max) - height[i]
        if (l_max < r_max) {
            res += l_max - height[left];
            left++; 
        } else {
            res += r_max - height[right];
            right--;
        }

        }
        return res;
    }
};

拆解一下代码思路:
1、定义两个指针 (左、右指针)
2、定义左、右的边界最大值,并在移动中更新这两个max(用于计算比他低的差值——也就是存水量)。
3、比较2个指针所在位置的高度,然后谁小谁先移动(左++,右-- 两边收紧模式)
4、移动的同时计算差值(谁小谁先动,另一边一定比他高所以不用担心漏的问题)
ps:左右边界在移动中 ,高度都取max,所以在递增情况时,max即当前值,total便不会增加。 

假设一开始lmax大于rmax,则之后right会一直向左移动,直到rmax大于lmax。在这段时间内right所遍历的所有点都是左侧最高点lmax大于右侧最高点rmax的,所以只需要根据原则判断rmax与当前高度的关系就行。反之left右移,所经过的点只要判断lmax与当前高度的关系就行。

有点类似快排的走法

对比上面备忘录,ml【i】记录的是0到 i的最高柱子,mr【i】是i到结尾的最高柱子

 

双指针里面 l_max 和 r_max 代表的是 height[0..left] 和 height[right..end] 的最高柱子高度

对于这段代码

   if (l_max < r_max) {
            res += l_max - height[left];
            left++; 
        }

我们解释一下,为什么这样就可以去计算雨水了。

此时的 l_max 是 left 指针左边的最高柱子,但是 r_max 并不一定是 left 指针右边最高的柱子,这真的可以得到正确答案吗?

其实这个问题要这么思考,我们只在乎 min(l_max, r_max)。对于上图的情况,我们已经知道 l_max < r_max 了,至于这个 r_max 是不是右边最大的,不重要,重要的是 height[i] 能够装的水只和 l_max 有关。

法4:

写到这的时候又发现了个很牛的写法,不过照着代码走一遍就能懂为什么这么写了

看接完水的数字很像一个山峰,左边递增,右边递减(不严格的递增递减),那么久很好办了。找到这个找到最大值索引,左右各自遍历一遍 两边都维护一个值来表示之前的最大值以保证单调性, 如果比最大值小,雨水量就加上这个差值, 如果大于等于,就更新最大值。

  1. 先遍历一遍,找到最高点,记录高度imax和坐标h
  2. 再分别从两边两个指针往中间扫,以当前遍历中遇到的最高点的高度为标准stan
  3. 再往中间走,如果当前的坐标高度小于stan,就说明当前坐标能接的雨水为stan-height[i]
class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()==0) return 0;
        int imax=-1,h;
        for(int i=0;i<height.size();i++)//找到最高点和它的高度
        {
            if(height[i]>imax)
            {
                imax=height[i];
                h=i;
            }
        }
        int stan=0,ans=0;
        for(int i=0;i<h;i++)//从前遍历到最高点
        {
            if(height[i]>stan)
            {
                stan=height[i];
                continue;
            }
            else  ans+=stan-height[i];
        }
        stan=0;//stan归零
        for(int i=height.size()-1;i>h;i--)//从后遍历到最高点
        {
            if(height[i]>stan)
            {
                stan=height[i];
                continue;
            }
            else ans+=stan-height[i];
        }   
        return ans;  
    }
};

法5:

还有单调栈的写法这里就不写了

贴个题解日后回来看

42. 接雨水 - 力扣(Leetcode)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/23333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【26-业务开发-基础业务-品牌管理-图片管理-上传图片功能实现-基于阿里云OSS服务-解决跨域问题-设置跨域规则-修改ACL权限为公共读】

一.知识回顾 【0.三高商城系统的专题专栏都帮你整理好了&#xff0c;请点击这里&#xff01;】 【1-系统架构演进过程】 【2-微服务系统架构需求】 【3-高性能、高并发、高可用的三高商城系统项目介绍】 【4-Linux云服务器上安装Docker】 【5-Docker安装部署MySQL和Redis服务】…

springmvc源码之Web上下文初始化

系列文章目录 springmvc源码之Web上下文初始化 文章目录系列文章目录Web上下文初始化ContextLoaderListenerinitWebApplicationContext初始化创建WebApplicationContext上下文Web上下文初始化 web上下文与SerlvetContext的生命周期应该是相同的&#xff0c;springmvc中的web上…

公众号免费查题

公众号免费查题 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#xff09; 题…

Flink概念及应用场景

1、Flink实时应用场景 Flink在实时计算领域内的主要应用场景主要分为四类&#xff1a; 实时数据同步流式ETL实时数据分析复杂事件处理2、实时数据体系架构 实时数据体现大致分为三类场景&#xff1a; 流量类业务类特征类在数据模型上&#xff0c;流量类是扁平化的宽表&…

论文管理系统(用户列表显示功能)

上次我们已经实现了论文管理系登录功能,这次我们要实现登录之后的跳转到首页,并且让首页列表显示出数据库的信息并在Mapper中写入模糊查询功能语句,这次我们不使用MybatisPlus写这个功能,这次使用Mybatis来写,区别就是Plus是继承于<BaseDAO>Mapper,而Mybatis则是我们通过…

JAVASE(复习)——static

static 关键字是静态的意思,是Java中的一个修饰符,可以修饰成员方法,成员变量 一、被static修饰的特点 被类的所有对象共享&#xff08;在堆内存共享&#xff09; 随着类的加载而加载&#xff0c;优先于对象存在&#xff08;就是你加载类的时候就加载了&#xff09; 可以通过…

@Transactional注解为何会失效

使用 Transactional 注解能保证方法内多个数据库操作要么同时成功、要么同时失败。但是有很多细节需要注意&#xff0c;不然Transactional可能会失效。 1.注解应用在非 public 的方法上 如果Transactional注解应用在非public 修饰的方法上&#xff0c;Transactional将会失效。…

FLink源码 1.13 3 种 命令客户端 Generic CLI 、 yarn-cluster、DefaultCLI使用

先说结论:对于三种Cli,Generic CLI mode、yarn-cluster mode、default mode,无法同时使用,源码使用顺序为Generic CLI mode优先判断,接着是 yarn-cluster mode ,最后是default mode,所以对于三种Cli的参数,不能混用,否则会出现命令不生效的情况,具体使用以及源码见下…

【滤波跟踪】基于matlab扩展卡尔曼滤波的无人机路径跟踪【含Matlab源码 2236期】

⛄一、EKF算法简介 扩展卡尔曼滤波是利用泰勒级数展开方法将非线性滤波问题转化成近似的线性滤波问题,利用线性滤波的理论求解非线性滤波问题的次优滤波算法。其系统的状态方程和量测方程分别如式(1)、式(2)所示: 式中,X(k)为n维的随机状态向量序列,Z(k)为n维的随机量测向量序…

Matlab 对连续时间信号的运算

Matlab 对连续时间信号的运算 1、连续时间系统零状态响应 题目&函数说明 连续时间系统的微分方程为 y"(t) 4y’(t) 3y(t) f’(t) 2f(t) 当 输入信号 f(t) 20e-2tu(t) 时&#xff0c;初始值 y(0-) 2, y’(0-) 1, 求系统的零状态响应 Matlab 库函数中的 lsim(…

GNN Tensorflow packages

tf framework定义 tf.name_scope()函数 tf.name_scope(name)&#xff0c;用于定义python op的上下文管理器。 此上下文管理器将推送名称范围&#xff0c;这将使其中添加的所有操作的名称带有前缀。 例如&#xff0c;定义一个新的Python op my_op&#xff1a; def my_op(a,…

scratch踢足球 电子学会图形化编程scratch等级考试一级真题和答案解析2022年9月

目录 scratch踢足球 一、题目要求 1、准备工作 2、功能实现 二、案例分析

代码随想录算法训练营第三十九天| LeetCode62. 不同路径、LeetCode63. 不同路径 II

一、LeetCode62. 不同路径 1&#xff1a;题目描述&#xff08;62. 不同路径&#xff09; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下…

python 视角下的 6 大程序设计原则

众所周知&#xff0c;python 是面向对象的语言。 但大多数人学习 python 只是为了写出“能够实现某些任务的自动化脚本”&#xff0c;因此&#xff0c;python 更令人熟知的是它脚本语言的身份。 那么&#xff0c;更近一步&#xff0c;如果使用 python 实现并维护一个大的项目…

基于SpringBoot的CSGO赛事管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目…

为研发效能而生|一场与 Serverless 的博弈

2022 年 11 月 3 日&#xff0c;第三届云原生编程挑战赛即将迎来终极答辩&#xff0c;18 支战队、32 位云原生开发者入围决赛&#xff0c;精彩即将开启。 云原生编程挑战赛项目组特别策划了《登顶之路》系列选手访谈&#xff0c;期待通过参赛选手的故事&#xff0c;看到更加生…

第1章 计算机系统概述

1.1 操作系统的基本概念 1.1.1 操作系统的概念 操作系统是计算机系统中最基本的系统软件。 操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是指控制和管理整个计算机系统的硬件与软件资源&#xff0c;合理地组织、调度计算机的工作与资源的分配&#xff0c;进…

锐捷端口安全实验配置

端口安全分为IPMAC绑定、仅IP绑定、仅MAC绑定 配置端口安全是注意事项 如果设置了IPMAC绑定或者仅IP绑定&#xff0c;该交换机还会动态学习下联用户的MAC地址 如果要让IPMAC绑定或者仅IP绑定的用户生效&#xff0c;需要先让端口安全学习到用户的MAC地址&#xff0c;负责绑定不生…

如何参与一个开源项目!

今天教大家如何给开源项目提交pr&#xff0c;成为一名开源贡献者。pr是 Pull Request 的缩写&#xff0c;当你在github上发现一个不错的开源项目&#xff0c;你可以将其fork到自己的仓库&#xff0c;然后再改动一写代码&#xff0c;再提交上去&#xff0c;如果项目管理员觉得你…

【建议收藏】回收站数据恢复如何操作?3个方案帮你恢复删除的文件

在使用电脑时&#xff0c;我们经常会清理不需要的文件数据。电脑回收站被清空了&#xff0c;但是里面有我们重要的数据&#xff0c;回收站数据恢复如何操作&#xff1f;不如试试下面的3个方案&#xff0c;一起来了解一下回收站数据恢复吧&#xff01; 一、注册表恢复回收站数据…