LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)
题目四:接雨水(No. 43)
题目链接:https://leetcode.cn/problems/trapping-rain-water/description/?envType=study-plan-v2&envId=top-100-liked
难度:困难
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
题解
首先来观察对于一个格子来说,它的容量是多少:
比如上图中的红色部分,它存储水的容量其实就是 左边的最大高度 和 右边的最大高度 的 最小值,减去这里的格子高度(案例中为 0),在上面的案例中,左边的最高高度为 2,右边的最高高度为 3。
所以本题的暴力解法就比较好想了,找到其左边最大高度,再找到其右边的最大高度,通过上面的规律进行运算。
class Solution {public int trap(int[] height) {int res = 0;for (int i = 1; i < height.length; i++) {int left = i - 1;int right = i + 1;int maxLeft = 0;int maxRight = 0;while (left >= 0) { // 去左边寻找最大的maxLeft = Math.max(maxLeft, height[left--]);}while (right < height.length) { // 去右边寻找最大的maxRight = Math.max(maxRight, height[right++]);}int temp = Math.min(maxLeft, maxRight) - height[i]; // 执行运算逻辑res += temp > 0 ? temp : 0;}return res;}
}
这个方法的时间复杂度无疑是非常高的,因为每遍历到一个节点的时候都需要遍历整张表,接下来考虑如何对上面的方法进行优化。
首先来看左边的最大值,考虑一下,我们真的有必要每次去遍历来求得最大值吗?
可以将之前遍历过的节点的最大值保存下来,比如说这么一个高度数组 4,2,0,3,2,5
,我们从第二个数字开始遍历,当执行完这个数字的逻辑之后,就可以拿它和之前的最大高度作比较,用作下一个节点的左边最大高度,这样不断更新就能保证每次求得的都是准确的。
class Solution {public int trap(int[] height) {int res = 0;int maxLeft = height[0];for (int i = 1; i < height.length; i++) {int left = i - 1;int right = i + 1;int maxRight = 0;while (right < height.length) { // 找到右边最大高度maxRight = Math.max(maxRight, height[right++]);}int temp = Math.min(maxLeft, maxRight) - height[i];res += temp > 0 ? temp : 0;maxLeft = Math.max(height[i], maxLeft); // 维护一个更新的 maxLeft}return res;}
}
既然对左边可以进行优化,那对右边是否可以优化呢?
因为是从前向后遍历的,所以右边肯定不可能像左边这样简单的更新;所以可以考虑提前处理,如果从后向前遍历的话,就可以类似左边那样求出 每个节点 的右最大值,所以可以在进入循环之前,先遍历一次高度数组,求出一个存储着每个节点右最大值的数组。
class Solution {public int trap(int[] height) {int res = 0;int maxLeft = height[0];int k = 2;int[] maxRight = new int[height.length];int m = height[maxRight.length - 1];for (int j = maxRight.length - 2; j >= 0; j--) { // 从后向前遍历,维护一个存储每个节点最大值的数组maxRight[j] = m;m = Math.max(m, height[j]);}for (int i = 1; i < height.length; i++) {int temp = Math.min(maxLeft, maxRight[i]) - height[i]; // 使用数组中的值res += temp > 0 ? temp : 0;maxLeft = Math.max(height[i], maxLeft);}return res;}
}
题目一:无重复字符的最长字串(No. 3)
题目链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/?envType=study-plan-v2&envId=top-100-liked
题目难度:中等
给定一个字符串 s
,请你找出其中不含有重复字符的 最长
子串
的长度。
示例 1:
输入:s = "abcabcbb"
输出:3
解释: 因为无重复字符的最长子串是"abc",所以其长度为 3。
示例 2:
输入:s = "bbbbb"
输出:1
解释:因为无重复字符的最长子串是"b",所以其长度为 1。
示例 3:
输入:s = "pwwkew"
输出:3
解释:因为无重复字符的最长子串是"wke",所以其长度为 3。请注意,你的答案必须是子串的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
题解
首先来看第一种方法,提到最长子串,想到的第一个方法就是动态规划。
先来尝试找一下状态,题目中问的是没有重复字符的最长的子串,可以将某个节点的状态定义为:以这个节点为结尾的,不含有重复字符的字串的最大长度,这种方式在处理子串的问题中非常常见。
再来思考这个状态应该如何转移。对于一个节点其实有这些选择
- 从这个节点开始(重新开始)
- 接续前面的子串
本题接续前面子串的时候要考虑不能含有重复的元素,所以并不像之前的题目那样就简单的做一个加一,但总而言之状态是可以转移的,那就来尝试一下。
先来确定 dp 数组,根据上面的推理,dp 数组只需要一维就即可, dp[i]
的含义为以 s.charAt(i)
为结尾的,不含有重复子串的最大长度。
然后就是确定状态转移方程了,对于一个下标为 x 的节点,它所代表的子串就是从 x - dp[i]
到 x 这个范围内内容;比如我们现在遍历到了下标为 x + 1 的节点,如果想要接续上前面的内容,就需要上面的范围中不含有 s.charAt(x + 1)
这个字符,此时需要通过遍历来确定是否有这个字符。
如果没有发现,那 dp[i] = dp[i - 1] + 1
如果发现了重复的字符串,比如下面的情况
此时要求得的是绿色的 b 的值,前一个节点最大子串的长度为 3,但当遍历到图中粉色的节点的时候发现了重复,此时 b 的长度应该为多少呢?是 2
画个图来更直观的了解一下:
当发现了相同的节点之后,这个值应该就是从被发现的位置索引 + 1 一直到新节点的长度
此时就确定了两种情况的递推公式,下面来讨论一下初始化
因为需要前一个节点的情况,所以 dp[0]
是要被初始化的,按照上面的含义,该位置应该被初始化为 1。
class Solution {public int lengthOfLongestSubstring(String s) {if (s.length() == 0) return 0;int[] dp = new int[s.length()];dp[0] = 1;int res = 1;for (int i = 1; i < s.length(); i++) {int field = dp[i - 1]; // 需要查询重复的范围char c = s.charAt(i);boolean findSame = false; // 是否找到了相同的节点while (field > 0) {int index = i - (field--);if (s.charAt(index) == c) { // 找到了相同的节点int m = i - index - 1 + 1;dp[i] = m;findSame = true;break;}}if (!findSame) dp[i] = dp[i - 1] + 1;res = Math.max(dp[i], res); // 动态更新 res}return res;}
}
接下来来看一下滑动窗口方案
所谓的滑动窗口其实就是用索引去限定一个范围,通过不断移动左范围和右范围的方式来调整窗口的范围,从而收集信息。
当滑动窗口的内容满足要求的时候就不断 移动右边界,来扩展滑动窗口的大小,如果发现不满足要求,也就是出现了重复的情况,就通过 收缩左边界 最终使得窗口内的元素始终满足要求,然后记录滑动窗口的长度。
滑动窗口方法的核心和上面的动态规划方法相同,都是通过遍历每个元素为 右节点 的情况,来不断更新最大值。
class Solution {char[] charArray;public int lengthOfLongestSubstring(String s) {if (s.length() == 0) return 0;charArray = s.toCharArray();int left = 0, right; // 左范围,右范围int res = 1;for (int i = 1; i < charArray.length; i++) {right = i;while (examine(left, right, charArray[i])) {left++;}res = Math.max(right - left + 1, res); // 更新结果}return res;}// 检测范围内是否有和此时右范围相同的元素public boolean examine(int left, int right, char c) {for (int i = left; i <= right - 1; i++) {if (charArray[i] == c) return true;}return false;}
}
相关文章:

LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)
题目四:接雨水(No. 43) 题目链接:https://leetcode.cn/problems/trapping-rain-water/description/?envTypestudy-plan-v2&envIdtop-100-liked 难度:困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&am…...

常用的深度学习自动标注软件
0. 简介 自动标注软件是一个非常节省人力资源的操作,而随着深度学习的发展,这些自动化标定软件也越来越多。本文章将会着重介绍其中比较经典的自动标注软件 1. AutoLabelImg AutoLabelImg 除了labelimg的初始功能外,额外包含十多种辅助标注…...

选择程序员是为什么?
本章节是关于为什么会选择一名程序员的经验分享 首先,我为什么会选择这个方向,可能是因为钱多,学东西不就是为了赚钱嘛?这是一点,不过最让我接收这个行业的是好奇世界的新大陆,可以简单的说就是,…...

线程池参数如何设置
线程池参数设置 hello丫,各位小伙伴们,好久不见了! 下面,我们先来复习一下线程池的参数 1、线程池参数有哪些? corePoolSize(核心线程数):线程池中的常驻核心线程数。即使这些线程…...

qt环境搭建-镜像源安装Qt Creator(5.15.2)以及配置环境变量
前言: 版本:5.15.2 镜像源:ustc与清华 纯小白,找了半天的镜像源安装qtcreator,搞了半天结果安装的是最新的,太新的对小白很不友好,bug比较多,支持的系统也不全,口碑不…...

SQL Server详细安装使用教程
1.安装环境 现阶段基本不用SQL Server数据库了,看到有这样的分析话题,就把多年前的存货发一下,大家也可以讨论看看,思路上希望还有价值。 SQL Server 2008 R2有32位版本和64位版本,32位版本可以安装在Windows XP及以上…...

深度解读C++17中的std::string_view:解锁字符串处理的新境界
深入研究C17中的std::string_view:解锁字符串处理的新境界 一、简介二、std::string_view的基础知识2.1、构造函数2.2、成员函数 三、std::string_view为什么性能高?四、std::string_view的使用陷阱五、std::string_view源码解析六、总结 一、简介 C中有…...

汇编基础-----常见命令基本使用
汇编基础-----常见命令基本使用 MOV:将数据从一个位置复制到另一个位置。 MOV destination, source例如: MOV RAX, RBX ; 将RBX寄存器中的值复制到RAX寄存器中ADD/SUB:将两个操作数相加或相减。 ADD destination, source SUB destinatio…...

科研学习|可视化——相关性结果的可视化
一、相关性分析介绍 相关性分析是指研究两种或者两种以上的变量之间相关关系的统计分析方法,一般分析步骤为: 1)判断变量间是否存在关联;2)分析关联关系(线性/非线性)、关联方向(正相…...

MapReduce过程解析
一、Map过程解析 Read阶段:MapTask通过用户编写的RecordReader,从输入的InputSplit中解析出一个个key/value。Map阶段:将解析出的key/value交给用户编写的Map()函数处理,并产生一系列的key/value。Collect阶段:在用户编…...

速看!这8道嵌入式面试题你都会吗?
大家好,我是知微! 正逢求职季,分享一些嵌入式面试当中经常会遇到的题目,希望这些干货对小伙伴们面试有用哦! 1、介绍一下static关键字的作用 在C语言中,static 关键字有几种不同的作用,根据其…...

基于SSM的电影网站(有报告)。Javaee项目。ssm项目。
演示视频: 基于SSM的电影网站(有报告)。Javaee项目。ssm项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过Spring SpringMv…...

SOCKS代理是如何提高网络性能和兼容性的?
SOCKS代理作为一种网络协议中间件,不仅在提升网络隐私和安全性方面发挥着重要作用,也在提高网络性能和兼容性方面有着不容忽视的影响🚀。本文将深入探讨SOCKS代理如何通过减少网络延迟🚀、优化数据传输🔄、提高跨平台兼…...

好菜每回味道不同--建造者模式
1.1 炒菜没放盐 中餐,老板需要每次炒菜,每次炒出来的味道都有可能不同。麦当劳、肯德基这些不过百年的洋快餐却能在有千年饮食文化的中国发展的那么好呢?是因为你不管何时何地在哪里吃味道都一样,而鱼香肉丝在我们中餐却可以吃出上…...

RuoYi-Cloud下载与运行
一、源码下载 若依官网:RuoYi 若依官方网站 鼠标放到"源码地址"上,点击"RuoYi-Cloud 微服务版"。 跳转至Gitee页面,点击"克隆/下载",复制HTTPS链接即可。 源码地址为:https://gitee.com/y_project/RuoYi-Cloud.git 点击复制 打开IDEA,选…...

Vue2.x计算属性
1.计算属性 在Vue 插值表达式内实现一些操作其实非常便利,但如果表达式的逻辑过于复杂,会让插值过于臃肿且难以维护。这时可以考虑使用Vue的计算属性 1.1 不使用计算属性的例子 <!DOCTYPE html> <html><head><meta charset"…...

Vue中使用require.context()自动引入组件和自动生成路由的方法介绍
目录 一、自动引入组件 1、语法 2、使用 2.1、在compoents文件下随便创建index.js文件 2.2、mian.js引入该js 二、自动生成路由 1、示例: 2、使用 2.1、在router文件下随便创建autoRouter.js文件 2.2、在router文件下index.js文件中引入autoRouter.js文件…...

【炒股Zero To Hero】MACD金叉死叉到底是否有效,加上这个指标回报率增加197倍
移动平均收敛散度(MACD - Moving Average Convergence Divergence)是一种趋势跟踪动量指标,显示了证券价格的两个移动平均之间的关系。它用于识别趋势的方向和强度,属于技术分析中振荡器的一类。 MACD如何衡量股票及其趋势 有两…...

Linux网络名称空间和虚拟机有何区别
在Linux系统中,网络名称空间和虚拟机都是实现资源隔离和虚拟化的技术,但它们在设计理念、实现机制、资源消耗、使用场景等方面存在着显著的区别。本文旨在全方位、系统性地分析这两种技术的区别。🔍 1. 设计理念与实现机制 1.1. 网络名称空…...

【UE Niagara】蓝图获取粒子数据
目录 效果 步骤 一、创建粒子 二、创建蓝图接收Niagara参数 效果 步骤 一、创建粒子 1. 新建一个Niagara发射器,使用Empty模板,打开后先添加“Spawn Rate”模块,这里设置粒子生成速率为0.7 在“Initialize Particle”模块中设置粒子颜色…...

更改el-cascade默认的value和label的键值
后端返回的树结构中,label的key不是el-cascade默认的label,我需要改成对应的字段,但是一直没有成功,我也在文档中找到了说明,但是我没注意这是在props中改,导致一直不成功 这是我一开始错误的写法…...

2024邮件工单系统排行揭晓:出海必备新宠
2024年各大榜单结果纷纷出炉,一起来看看2024十大邮件工单系统最新排行吧! 2024十大邮件工单系统 1、Zoho Desk;2、FreshDesk;3、Service Desk Plus;4、Help Scout;5、Helpshift;6、HongDans&am…...

java题目17:以m行n列二维数组为参数进行方法调用,分别计算二维数组各列元素之和,返回并输出计算结果(MethodCalls17)
每日小语 伟大企业的一项特质是“利润之上的追求”。——段永平 思考 方法调用 方法调用是通过在代码中使用方法名和参数列表来实现的。 public class MethodExample {public static void main(String[] args) {// 调用方法add,并传入两个参数int sum add(3, 5…...

Python中Python-docx 包的run介绍
先对run做一个简单地介绍。每个paragraph对象都包含一个run对象的列表。举例: 这是一个简短的段落。 from docx import Document doc Document("1.docx") #上面这段话保存在1.docx中 print("这一段的run个数是:",len(doc.paragr…...

vue2升级到vue3的一些使用注意事项记录(三)
更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码:…...

SwiftUI Swift 显示隐藏系统顶部状态栏
Show me the code // // TestHideSystemTopBar.swift // pandabill // // Created by 朱洪苇 on 2024/4/1. //import SwiftUIstruct TestHideSystemTopBar: View {State private var isStatusBarHidden falsevar body: some View {Button {withAnimation {self.isStatusBa…...

PowerJob 分布式任务调度简介
目录 适用场景 设计目标 PowerJob 功能全景 任务调度 工作流 分布式计算 动态容器 什么是动态容器? 使用场景 可维护性和灵活性的完美结合 实时日志&在线运维 PowerJob 系统组件 PowerJob 应用场景 PowerJob 的优势 PowerJob(原OhMyScheduler&…...

Java——数组练习
目录 一.数组转字符串 二.数组拷贝 三.求数组中元素的平均值 四.查找数组中指定元素(顺序查找) 五.查找数组中指定元素(二分查找) 六.数组排序(冒泡排序) 七.数组逆序 一.数组转字符串 代码示例: import java.util.Arrays int[] arr {1,2,3,4,5,6}; String…...

波士顿房价预测案例(python scikit-learn)---多元线性回归(多角度实验分析)
波士顿房价预测案例(python scikit-learn)—多元线性回归(多角度实验分析) 这次实验,我们主要从以下几个方面介绍: 一、相关框架介绍 二、数据集介绍 三、实验结果-优化算法对比实验,数据标准化对比实验࿰…...

在 Queue 中 poll()和 remove()有什么区别?
在Java的Queue接口中,poll()和remove()方法都用于从队列中删除并返回队列的头部元素,但是它们在队列为空时的行为有所不同。 poll()方法:当队列为空时,poll()方法会返回null,而不会抛出异常。这是它的主要特点&#x…...