代码随想录刷题day59|下一个更大元素II接雨水

文章目录

  • day59学习内容
  • 一、下一个更大元素II
    • 1.1、思路
    • 1.2、代码
  • 二、接雨水
    • 2.1、双指针法
      • 2.1.1、思路
      • 2.1.2、代码
    • 2.2、单调栈法
      • 2.2.1、思路
      • 2.2.2、代码
  • 总结
    • 1.感想
    • 2.思维导图


day59学习内容

day59主要内容

  • 下一个更大元素II
  • 接雨水

声明
本文思路和文字,引用自《代码随想录》


一、下一个更大元素II

503.原题链接

1.1、思路

  1. 数组的循环遍历
    因为数组是循环的,所以单纯遍历一次数组可能找不到所有元素的下一个更大值,特别是数组的前半部分的元素可能需要比较数组后半部分的元素。因此,可以通过遍历数组两次来解决这个问题,实质上是模拟了数组的循环。

  2. 使用取模操作处理索引
    遍历两倍长度的数组并使用取模操作(i % size),可以有效地模拟循环数组的行为,这样就可以保证每个元素都能被比较到。也可以避免数组下标越界~

  3. 栈的利用

  • 当遍历到一个新元素时,如果它比栈顶元素索引对应的数组值大,说明找到了栈顶索引的下一个更大元素。
  • 更新结果数组,将找到的更大元素赋值给对应的索引位置。
  • 弹出栈顶元素(已经找到下一个更大值)。
  • 将当前元素的索引压入栈中以供后续比较。
  1. 维护栈的单调性
    保持栈的单调递减顺序(栈底到栈顶)。这样,当遇到一个较大的元素时,可以直接解决栈中所有比它小的元素的下一个更大元素问题。

  2. 最终结果的输出
    遍历结束后,栈中可能还有一些元素,它们没有更大的元素,它们在结果数组中对应的值会是初始设置的-1(如果未找到更大值)。遍历完两倍长度后,返回结果数组。

1.2、代码

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        // 检查数组是否为空或长度不大于1,这种情况下无法找到任何元素的下一个更大元素
        if(nums == null || nums.length <= 1) {
            return new int[]{-1}; // 返回包含单个-1的数组,表示没有下一个更大元素
        }
        
        int size = nums.length; // 获取数组长度
        int[] result = new int[size]; // 创建结果数组,用来存放每个元素的下一个更大元素
        Arrays.fill(result, -1); // 默认填充为-1,表示如果没有找到更大的元素就保留这个值
        
        Stack<Integer> st = new Stack<>(); // 创建一个栈,用于存放数组元素的索引
        
        // 遍历数组两次,模拟循环数组的效果
        for(int i = 0; i < 2 * size; i++) {
            // 当栈不为空且当前元素大于栈顶元素对应的数组值时
            while(!st.empty() && nums[i % size] > nums[st.peek()]) {
                result[st.peek()] = nums[i % size]; // 将当前元素设置为栈顶元素的下一个更大元素
                st.pop(); // 将栈顶元素弹出,因为我们已经找到了它的下一个更大元素
            }
            if (i < size) {
                st.push(i); // 在第一轮遍历中,将每个元素的索引压入栈中
            }
        }
        
        return result; // 返回包含下一个更大元素的结果数组
    }
}

二、接雨水

42.原题链接
本题可以用双指针法和单调栈法解决。

2.1、双指针法

2.1.1、思路

  1. 初始化
    定义两个指针leftright,分别指向数组的开始和结束。同时,定义两个变量maxLeftmaxRight,初始值设为数组两端的值。这两个变量用来跟踪到目前为止遇到的最高的左边和右边的柱子。

  2. 从两边向中心遍历
    双指针法核心在于从两端向中间遍历,通过比较leftright两个指针所指向的柱子的高度,决定是移动left指针还是right指针。

  • 如果height[left] < height[right],则处理left指针,因为此时左侧柱子较矮,决定了水的高度上限由左侧控制。
  • 如果height[right] <= height[left],则处理right指针,因为此时右侧柱子较矮或者和左侧柱子相等,水的高度上限由右侧控制。
  1. 更新最大高度和计算积水
  • 当移动left指针时:

    • 更新maxLeft为到目前为止遇到的最大左侧柱子高度。
    • 计算当前位置可能的积水量,即maxLeft - height[left],如果这个值为正,累加到总积水量中。
    • left指针向中心移动。
  • 当移动right指针时:

    • 更新maxRight为到目前为止遇到的最大右侧柱子高度。
    • 计算当前位置可能的积水量,即maxRight - height[right],如果这个值为正,累加到总积水量中。
    • right指针向中心移动。
  1. 结束条件
    left指针超过right指针时,遍历结束。此时,所有可能的积水位置已经计算完成。

  2. 计算总积水量
    遍历完成后,累计的积水量即为所有位置积水量之和。

这种方法的优势在于只需遍历一次数组,且不需要额外的空间(除了几个辅助变量外),从而使得时间复杂度为O(n),空间复杂度为O(1)。

2.1.2、代码

class Solution {
    public int trap(int[] height) {
        int length = height.length;
        // 如果数组长度小于3,没有足够的柱子形成低洼地积水
        if (length <= 2) return 0;

        // maxLeft数组记录每个位置左侧的最大高度
        int[] maxLeft = new int[length];
        // maxRight数组记录每个位置右侧的最大高度
        int[] maxRight = new int[length];

        // 初始化maxLeft数组的第一个值为第一个柱子的高度
        maxLeft[0] = height[0];
        // 从左向右计算每个位置的左侧最大高度
        for (int i = 1; i < length; i++) {
            maxLeft[i] = Math.max(height[i], maxLeft[i-1]);
        }

        // 初始化maxRight数组的最后一个值为最后一个柱子的高度
        maxRight[length - 1] = height[length - 1];
        // 从右向左计算每个位置的右侧最大高度
        for(int i = length - 2; i >= 0; i--) {
            maxRight[i] = Math.max(height[i], maxRight[i+1]);
        }

        int sum = 0; // 用于累计所有位置的积水量
        // 遍历每个位置,计算积水量
        for (int i = 0; i < length; i++) {
            // 当前位置的积水量由左右两侧最小的最大高度决定,减去当前高度
            int count = Math.min(maxLeft[i], maxRight[i]) - height[i];
            // 如果计算结果为正,说明有积水,累加到总量中
            if (count > 0) sum += count;
        }
        return sum; // 返回计算得到的总积水量
    }
}

2.2、单调栈法

2.2.1、思路

  1. 理解问题
    这个问题涉及一个数组,数组的每个元素表示柱子的高度。需要计算在柱子间由于高度差产生的低洼地方能积累多少水。因此,核心问题是找到每个柱子左右两边比它高的柱子,以决定这个位置能积多少水。

  2. 使用栈来跟踪柱子的索引
    使用栈来存储那些可能形成凹形结构的柱子的索引。栈的使用是因为它能够帮助我们保持一个正在处理的元素的顺序,这些元素可能会在之后形成容器的一部分。

  3. 迭代遍历数组
    从数组的第一个元素开始,对每个元素执行以下操作:

  • 如果当前柱子比栈顶柱子矮或相等,就把当前柱子的索引推入栈中。
  • 如果当前柱子比栈顶柱子高,表明我们可能找到了一个凹槽的右边界,可以开始计算水量。
  1. 计算积水量
    当找到一个可能的右边界时(当前柱子比栈顶柱子高):
  • 将栈顶索引弹出,这代表低洼处(凹槽)的底部。
  • 如果栈不为空,查看新的栈顶,这是凹槽的左边界。
  • 使用左边界和当前索引(右边界),以及底部索引计算水的体积:
    • 宽度为右边界和左边界之间的距离减一。
    • 高度为左右边界的较小高度减去底部的高度。
    • 体积即为宽度乘以高度(如果计算结果为正)。
  • 继续弹出栈顶元素和计算,直到当前柱子不再比栈顶柱子高或栈为空。
  1. 重复过程
    继续向右遍历直到数组结束,每个柱子都尝试进行上述判断和计算。

  2. 总结结果
    最终,所有积水的体积加起来就是解决问题的答案。

2.2.2、代码

class Solution {
    public int trap(int[] height) {
        int size = height.length;
        // 如果数组长度小于3,则不可能形成任何可以积水的凹槽
        if (size <= 2) return 0;

        Stack<Integer> stack = new Stack<Integer>();
        // 初始时,将第一个元素索引压入栈
        stack.push(0);

        int sum = 0;  // 用于累计所有的积水量
        // 从第二个元素开始遍历数组
        for (int index = 1; index < size; index++) {
            // 检查栈顶元素与当前元素的关系
            while (!stack.isEmpty() && height[index] > height[stack.peek()]) {
                // 当前元素高于栈顶元素,可能形成一个凹槽
                int mid = stack.pop(); // 弹出凹槽底部
                // 确保栈不为空,以便我们有左边界
                if (!stack.isEmpty()) {
                    int left = stack.peek();  // 获取凹槽左边界
                    // 计算可能的积水高度
                    int h = Math.min(height[left], height[index]) - height[mid];
                    // 计算宽度
                    int w = index - left - 1;
                    // 计算当前凹槽的积水量
                    int hold = h * w;
                    // 如果计算结果为正,则添加到总积水量
                    if (hold > 0) sum += hold;
                }
            }
            // 若当前元素高度等于栈顶元素高度,则更新栈顶,保持最右的索引
            if (!stack.isEmpty() && height[index] == height[stack.peek()]) {
                stack.pop();
            }
            // 将当前索引压入栈中
            stack.push(index);
        }

        // 返回计算得到的总积水量
        return sum;
    }
}

总结

1.感想

  • 明天第60天了,一刷要结束了。

2.思维导图

本文思路引用自代码随想录,感谢代码随想录作者。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从零到一品牌电商私域流量代运营规划方案

【干货资料持续更新&#xff0c;以防走丢】 从零到一品牌电商私域流量代运营规划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 PPT共50页&#xff08;完整资料包含以下内容&#xff09; 目录 私域运营方案&#xff1a; 一、项目背景与目标 - 开创数智化…

华为路由器基于接口限速

一、背景 ISP与企业内网通过华为路由器接入Internet时,当大量流量进入路由器时,可能会因为带宽不足产生拥塞,导致丢包,严重影响用户上网体验。对于此需要对网络流量进行限制,其方式通常有防火墙带宽策略、路由器基于接口限速等。 二、华为路由器基于接口限速方式 在路由…

Docker 部署 MongoDB 数据库

文章目录 官网地址docker 网络mongod.conf部署 MongoDB部署 mongo-expressdocker-compose.ymlMongoDB shell 官网地址 https://www.mongodb.com/zh-cn docker 网络 # 创建 mongo_network 网络 docker network create mongo_network # 查看网络 docker network list # 容器连…

RT-Thread在Win10下编译出现 unsupported pickle protocol: 5解决方案

调试背景&#xff1a; 在WIN10下编译RT-Thread源码&#xff1a;对象处理器平台是Microchip SAMA5D27-SOM1-EK评估板。 unsupported pickle protocol: 5 编译出现报错:ValueError : unsupported pickle protocol: 5 $ scons scons: Reading SConscript files ... Newlib ver…

MySQL:执行一条查询语句期间发生了什么?

MySQL的架构分为两层&#xff0c;Server 层和存储引擎层 server层负责建立连接、分析和执行SQL&#xff0c;MySQL&#xff0c;MySQL大多数的核心功能模块都在在这里实现&#xff0c;下图上半部分都是server层做的事情&#xff0c;另外&#xff0c;所有的内置函数&#xff08;如…

在mini2440上编写linux应用程序、字符设备驱动程序的编写与编译

在mini2440上编写linux应用程序 结合前两篇的学习&#xff0c;一个linux操作系统已经在mini2440上运行起来了&#xff0c;结合交叉编译环境和nfs等工具&#xff0c;我们可以在mini2440上编写任何我们在linux系统编程中学到的应用程序。一个简要的多文件Makefile文件如下&#…

设计模式——2_9 模版方法(Template Method)

人们往往把任性也叫做自由&#xff0c;但是任性只是非理性的自由&#xff0c;人性的选择和自决都不是出于意志的理性&#xff0c;而是出于偶然的动机以及这种动机对感性外在世界的依赖 ——黑格尔 文章目录 定义图纸一个例子&#xff1a;从文件中获取信息分几步&#xff1f;Rea…

基于Spingboot+vue协同过滤音乐推荐管理系统

项目演示视频效果&#xff1a; 基于Spingbootvue协同过滤音乐推荐管理系统 基于Spingbootvue协同过滤音乐推荐管理系统 1、项目介绍 基于Springboot的音乐播放管理系统总共两个角色&#xff0c;用户和管理员。用户使用前端前台界面&#xff0c;管理员使用前端后台界面。 有推荐…

Golang内存、指针逃逸、垃圾回收机制概览

最近看到了一篇文章是关于go的内存、指针逃逸和垃圾回收机制的&#xff0c;发现自己并未很细致的了解过这方面的内容&#xff0c;于是在翻阅各种文章的情况下&#xff0c;写出了这篇总结&#xff0c;参考文章放在文末&#xff0c;可自取 内存 Go 语言使用一个自带的垃圾收集器…

【S32K3 入门系列】- ADC 模块简介(上)

一、 前言 对于 S32K3 系列的初学者来说&#xff0c;S32K3 系列的参考手册阅读难度是让人望而却步的&#xff0c;本系列将对 S32K3 系列的外设进行逐一介绍&#xff0c;对参考手册一些要点进行解析。本文旨在介绍 S32K3 系列的 ADC 模块&#xff0c; ADC&#xff08;Analog to…

node端导出excel-用请求排队来限流

需求 有一个会执行luckySheet脚本并且导出excel的node接口&#xff0c;会在每天凌晨执行&#xff0c;但是文件过大时会内存溢出 之前有用worker来实现多线程&#xff08;主要是避免变量污染&#xff09;&#xff0c;但这样只能保证主线程不卡死&#xff0c;几个子线程合起来占用…

MDC搭配ttl使用!!!

一、简介 MDC 介绍​ MDC&#xff08;Mapped Diagnostic Context&#xff0c;映射调试上下文&#xff09;是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map&#xff0c;可以往其中添加键值对。MDC 中包含的内容可以被…

使用yolov8 进行实例分割训练

1、基于windows 的ISAM标注 直接下载安装包&#xff0c;解压后即可使用 链接&#xff1a;https://pan.baidu.com/s/1u_6jk-7sj4CUK1DC0fDEXQ 提取码&#xff1a;c780 2、标注结果转yolo格式 通过ISAM标注后的json文件路径 原始json格式如下&#xff1a; ISAM.json 转 yolo.…

牛客2024 【牛客赛文X】春招冲刺 ONT34 加油站【中等 贪心 C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/a013a0691a0343aeb262ca1450d2fe4e 思路 贪心&#xff1a; 如果总的gas小于走完全程的cost&#xff0c;直接返回-1不需要再找了 如果确保了可以走完一圈之后&#xff0c;那么从index 0开始找&#xff0c; 当g…

【cygwin】工具安装apt-cyg

目录 下载安装查看是否安装成功安装软件 下载 git clone https://github.com/transcode-open/apt-cyg.git安装 cd apt-cyg mv apt-cyg /usr/local/bin/ 查看是否安装成功 apt-cyg --help安装软件 apt-cyg install nano

视频号小店怎么做?新手开店必备运营攻略,看这一篇就够了

大家好&#xff0c;我是电商笨笨熊 作为腾讯推出的电商项目&#xff0c;视频号小店在推出到现在一直都备受关注&#xff0c;同时也吸引了不少玩家入驻&#xff1b; 毕竟作为一个新平台、新市场&#xff0c;一个适合跑马圈地的红利平台&#xff0c;谁都想在这里分的一杯羹。 …

Linux debian gdb dump

1.开发背景 记录 debian 下应用程序崩溃调试方法 2.开发需求 程序越界可以定位到越界的位置附近 3.开发环境 debian 操作系统&#xff0c;如果不支持需要查看是否存在对应的可执行文件 4.实现步骤 4.1 设置 dump 输出大小 ulimit -c unlimited # 设置输出大小 生成core 文…

一个文生视频MoneyPrinterTurbo项目解析

最近抖音剪映发布了图文生成视频功能&#xff0c;同时百家号也有这个功能&#xff0c;这个可以看做是一个开源的实现&#xff0c;一起看看它的原理吧~ 一句话提示词 大模型生成文案 百家号生成视频效果 MoneyPrinterTurbo生成视频效果 天空为什么是蓝色的&#xff1f; 天空…

上位机图像处理和嵌入式模块部署(智能硬件的介绍)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前&#xff0c;用上位机软件虽然可以部署项目&#xff0c;但是它本身有自己的缺点&#xff0c;那就是稳定性差、价格贵。稳定性这部分&#xff0…

深度剖析扫雷游戏的各个知识点(2)

小伙伴们&#xff0c;大家好。这次继续上次的剖析扫雷游戏的知识点。 那么本次咱们主要是讲扫雷中的宏定义&#xff0c;也就是#define这些 首先#define是用来定义一个宏&#xff0c;后面就是类似于和变量一样的常量名&#xff0c;以及最后的数字就是它的值。 定义规则 #def…