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

【算法速刷(9/100)】LeetCode —— 42.接雨水

目录

自我解法

官方解法

解法一:动态规划、前后缀

解法二:单调栈

自我解法

这道题刚拿到的时候,第一时间的想法是将其想象成MC一样的方块世界,如何去生成水一样的去解决。后来发现有点复杂化了,因为题目只需要累计的结果数字,不需要额外的东西,但因为先入为主的观念,我还是选择了从低到高从左到右依次遍历的模拟法,这种方法的复杂度基本上是O(N * MaxHeight),实际也是十分低效,但私以为思路也是值得一分享,最后用例通过319/323。

class Solution {
public:
    int trap(vector<int>& height) {
        //从低到高从右向左打射线,找到间隔布置的两个方块,计算间隔,累加到结果上

        int res = 0;
        int n = height.size();
        int maxHeight = *max_element(height.begin(), height.end());
        
        for(int y = 1; y <= maxHeight; y++)
        {
            int lastBlockX = -1;
            for(int x = 0; x < n; x++)
            {
                if(height[x] >= y)
                {
                    if(lastBlockX != -1)
                    {
                        res += x - lastBlockX - 1;
                    }

                    lastBlockX = x;
                }
            }
        }

        return res;
    }
};

官方解法

官方的解法从数学角度考虑,并没有被这张图迷倒,而是在一维上解决问题,从这个角度出发,怎么也不会搞出来O(n * MaxHeight)的复杂度。

解法一:动态规划、前后缀

正所谓问题的关键在于找到关键的问题,这道题的关键也就是关键的问题:如何在数组的某个位置上计算其位置上能积累的水的高度?

实际上给定位置上能积水的高度就是“左边最高高度和右边最高高度”中的最小值。

我们可以通过构造两个数组来分别记录一个位置上左边和右边的最高高度,O(2N)的复杂度即可完成构造。注意构造的数组的值,不应低于当前位置的高度,假设左边高度始终低于当前高度,则在数组中记录为当前位置的高度,这样可以保证构造的数组减去原数组中当前位置高度不会出现负值。

最后指定一个位置,其能积累的水高度就是 min(leftMaxH, rightMaxH) - currentH

这个算式的值域 >= 0

最后时间复杂度即为O(3n) = O(n),漂亮滴很呐

class Solution {
public:
    int trap(vector<int>& height) {
        //预处理每个方块的左边和右边的最大数
        //该区块的纵向存水数量为左右墙中最低的高度减去自身

        int n = height.size();

        vector<int> leftMax(n);
        leftMax[0] = height[0];
        for(int i = 1; i < n; i++)
            leftMax[i] = max(leftMax[i - 1], height[i]);
        
        vector<int> rightMax(n);
        rightMax[n - 1] = height[n - 1];
        for(int i = n - 2; i >= 0; i--)
            rightMax[i] = max(rightMax[i + 1], height[i]);

        int res = 0;
        for(int i = 0; i < n; i++)
            res += min(leftMax[i], rightMax[i]) - height[i];
        
        return res;
    }
};

解法二:单调栈

除了上面比较常见的做法,还有就是使用单调栈。直观来说,通过维护一个单调递减的栈,可以通过进栈出栈来识别哪里是洼地,进栈则坡的方向是↘,出栈则坡的方向是↗。遇到递增则进行出栈操作,两个坐标之间的间隔必为空,即可以储水,累加到结果中。

需要注意的是,在出栈的操作中,需要三个高度

  • 第一个高度(栈顶),代表需要忽略的高度,因为是上一次已经处理过的,不忽略会导致重复累加
  • 第二个高度(次栈顶),代表当前蓄水的左边界高度,本次循环中不出栈,等下一次出栈。
  • 第三个高度(当前遍历到的),代表当前蓄水的右边界高度,尚未入栈。

如果是第一次做的话,还是很难想到这样写的,Debug会疯掉哒

class Solution {
public:
    int trap(vector<int>& height) {
        
        stack<int> stk;
        int n = height.size();
        int res = 0;

        for(int i = 0; i < n; i++)
        {
            while(!stk.empty() && height[i] > height[stk.top()])
            {
                int top = stk.top();
                stk.pop();
                if(stk.empty())
                    break;

                int left = stk.top();
                int space = i - left - 1;
                int h = min(height[left], height[i]) - height[top];
                res += space * h;
            }
            stk.push(i);
        }

        return res;
    }
};


http://www.kler.cn/a/390136.html

相关文章:

  • 万字长文分析函数式编程
  • 尽量通俗易懂地概述.Net U nity跨语言/跨平台相关知识
  • DeBiFormer实战:使用DeBiFormer实现图像分类任务(二)
  • react 中 FC 模块作用
  • 【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最大的数
  • Java学习--网络编程
  • 2024年9月青少年软件编程(C语言/C++)等级考试试卷(四级)
  • flask logger 使用 TimedRotatingFileHandler 报错 PermissionError 另一个程序正在使用此文件
  • NVR录像机汇聚管理EasyNVR多品牌NVR管理工具/设备:大华IPC摄像头局域网访问异常解决办法
  • 哪家云服务器好跑AI?瞄准AutoDL(附NVIDIA GPU 算力排名表)
  • Linux基础之病毒编写
  • Docker 操作指令
  • 如何设置el-date-picker的默认截止时间为“23:59:59”
  • 故事121
  • Ceph MDS高可用架构探索:从零到一构建多主一备MDS服务
  • (Go基础)Go的运行流程步骤与包的概念
  • 使用docker方式进行Oracle数据库的物理迁移(helowin/oracle_11g)
  • 【Linux系列】 环境配置文件合并的艺术:从`.env`到`.env.combined`
  • Radix Sorts
  • 音视频入门基础:FLV专题(25)——通过FFprobe显示FLV文件每个packet的信息
  • LeetCode每日一题3258---统计满足 K 约束的子字符串数量 I
  • pycharm连接oracle数据库查询数据
  • C# 多线程编程
  • 文本语义分块、RAG 系统的分块难题:小型语言模型如何找到最佳断点
  • Spring Boot框架下编程训练系统开发指南
  • 【Docker】Mac安装Docker Desktop导致磁盘剩余空间较少问题如何解决?