【LeetCode Hot100 子串】和为 k 的子数组、滑动窗口最大值、最小覆盖子串
子串
- 1. 和为 k 的子数组
- 题目描述
- 解题思路
- 主要思路
- 步骤
- 时间复杂度与空间复杂度
- 代码实现
- 2. 滑动窗口最大值
- 题目描述
- 解题思路
- 双端队列的原理:
- 优化步骤:
- Java实现
- 3. 最小覆盖子串
- 题目描述
- 解题思路
- 滑动窗口的基本思路:
- 具体步骤:
- 算法的关键点:
- Java实现
1. 和为 k 的子数组
题目描述
给定一个整数数组 nums 和一个整数 k,你需要在数组中找到连续子数组的个数,使得这些子数组的和等于 k。
解题思路
我们可以通过 前缀和 的方法来高效解决这个问题,结合 哈希表 来记录每个前缀和出现的次数,从而迅速计算出满足条件的子数组。
主要思路
-
前缀和的定义:
- 对于数组
nums,prefix[i]表示从nums[0]到nums[i-1]的和。也就是说,prefix[i] = nums[0] + nums[1] + ... + nums[i-1]。 - 子数组的和
nums[i..j]可以表示为:prefix[j+1] - prefix[i]。因此,如果我们希望找到nums[i..j]的和为k,那么只需要满足prefix[j+1] - prefix[i] = k。
- 对于数组
-
如何利用哈希表:
- 在遍历数组时,我们可以计算当前的前缀和
pre。 - 然后,我们通过
map.containsKey(pre - k)来判断是否存在一个前缀和为pre - k的位置,这样就找到了一个子数组和为k。 - 我们还需要维护一个哈希表
map,其中map.get(pre)表示当前前缀和pre出现的次数。这样做是为了确保我们能够计算出所有符合条件的子数组。
- 在遍历数组时,我们可以计算当前的前缀和
-
核心算法:
- 初始化哈希表
map,将0的计数初始化为 1,因为如果前缀和刚好为k,就意味着从数组起始位置开始的子数组和为k。 - 遍历数组并更新前缀和,并利用哈希表记录前缀和的出现次数。
- 初始化哈希表
步骤
-
初始化:
pre = 0表示当前的前缀和。cnt = 0表示符合条件的子数组数量。map存储前缀和及其出现次数,初始时将map.put(0, 1),即前缀和为 0 出现 1 次。
-
遍历数组:
- 对于每个元素,更新前缀和
pre。 - 检查哈希表中是否存在
pre - k,如果存在,说明从某个位置到当前位置的子数组和为k,则将其出现次数加到cnt中。 - 更新哈希表,将当前前缀和
pre出现的次数加 1。
- 对于每个元素,更新前缀和
-
返回结果:
- 遍历完所有元素后,
cnt中存储的就是符合条件的子数组数量。
- 遍历完所有元素后,
时间复杂度与空间复杂度
- 时间复杂度:
O(n),其中n是数组nums的长度。我们只需要遍历一次数组,同时进行常数时间的哈希表操作。 - 空间复杂度:
O(n),我们需要使用哈希表存储前缀和及其出现次数,最坏情况下哈希表的大小为n。
代码实现
class Solution {public int subarraySum(int[] nums, int k) {int len = nums.length;int pre = 0, cnt = 0;HashMap<Integer, Integer> map = new HashMap<>();map.put(0, 1); // 初始化,前缀和为0的有1个,这样做不会忽略掉“从数组起始位置开始的和为 k 的子数组”。for (int i = 0; i < len; i++) {pre += nums[i]; // 计算当前前缀和if (map.containsKey(pre - k)) { // 说明从某个位置到当前位置存在连续子数组和为 kcnt = cnt + map.get(pre - k); // 增加符合条件的子数组的数量}// 更新哈希表,记录当前前缀和出现的次数map.put(pre, map.getOrDefault(pre, 0) + 1); }return cnt; // 返回符合条件的子数组数量}
}
2. 滑动窗口最大值
题目描述
给定一个整数数组 nums 和一个滑动窗口的大小 k,请你在数组中找出每个滑动窗口的最大值,并返回一个数组。
解题思路
这道题目是一个典型的滑动窗口问题。直接暴力计算每个窗口中的最大值的时间复杂度是 O(n*k),这种做法在数据量较大的情况下效率较低。因此,我们可以使用 双端队列(Deque) 来优化这一过程。
双端队列的原理:
- 双端队列是一种支持从两端高效插入和删除的队列结构。我们可以利用它来存储数组元素的下标,并保持队列中的元素按照值的大小顺序排列。这样可以确保队列的第一个元素永远是当前窗口中的最大值。
优化步骤:
及时去掉无用数据,保证双端队列有序(当前数组>=队尾,弹出队尾;弹出队首不在窗口内的元素)
- 使用一个双端队列
q来存储窗口中的元素的下标。 - 保证队列中的元素下标对应的值是递减的,队列的首部始终是窗口的最大值。
- 每次移动窗口时:
- 入队操作:将新元素的下标加入队列,并从队列的尾部移除所有小于当前元素的值,以保证队列保持递减顺序。
- 出队操作:如果队列头部的元素已经不再在当前窗口范围内(即超出窗口的左边界),则将其从队列中移除。
- 记录结果:当窗口大小达到
k时,记录当前窗口的最大值,即队列头部的元素。
Java实现
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {int n = nums.length;Deque<Integer> q = new ArrayDeque<>(); // 存的是nums的下标int[] ans = new int[n - k + 1];for (int i = 0; i < n; i++) {// 1. 入队:保持队列中的元素递减while (!q.isEmpty() && nums[i] >= nums[q.getLast()]) {q.removeLast();}q.addLast(i); // 队列存的是下标// 2. 出队:如果队列的第一个元素不在窗口内,移除它if (i - q.getFirst() >= k) {q.removeFirst();}// 3. 存结果:当窗口大小达到k时,记录最大值if (i >= k - 1) {ans[i - k + 1] = nums[q.getFirst()];}}return ans;}
}
3. 最小覆盖子串
题目描述
给定字符串 s 和字符串 t,找到 s 中包含 t 中所有字符的最小子串。如果 s 中没有包含 t 中所有字符的子串,则返回空字符串。
解题思路
这是一个经典的滑动窗口问题。我们需要在字符串 s 中找到一个最小的子串,该子串包含了 t 中所有字符。最初我们可以考虑暴力解法,但暴力解法会超时,因此我们需要使用 滑动窗口 技巧来优化算法。
滑动窗口的基本思路:
- 滑动窗口的定义:我们维护一个窗口,窗口的大小是可变的,在窗口内包含了
t中的所有字符。 - 扩展窗口:从字符串
s的开始位置开始扩展窗口,逐步包含t中的字符。 - 收缩窗口:当窗口已经包含了
t中的所有字符时,尝试缩小窗口的大小,以找到更小的符合条件的子串。 - 窗口合法性:当窗口内包含所有
t中的字符时,窗口是合法的。
具体步骤:
- 使用两个指针
left和right表示滑动窗口的左右边界,初始化时都指向字符串s的开头。 - 使用两个哈希表
cntT和cntS来记录t中字符的出现频率和当前窗口中字符的出现频率。 - 当窗口包含
t中的所有字符时,尝试缩小窗口的左边界。 - 在每次扩展和收缩窗口时,更新当前的最小子串。
算法的关键点:
- 记录
t中所有字符的频率。 - 使用两个指针维护滑动窗口。
- 记录窗口内字符的频率并与
t中的字符频率进行比较。
Java实现
class Solution {public String minWindow(String s, String t) {int ansLeft = -1;int m = s.length();int ansRight = m;// 记录t的字符出现的次数Map<Character, Integer> cntT = new HashMap<>();for (char c : t.toCharArray()) {cntT.put(c, cntT.getOrDefault(c, 0) + 1);}// 记录s的字符出现的次数Map<Character, Integer> cntS = new HashMap<>();int left = 0;int formed = 0; // 记录s和t覆盖的字符的个数int required = cntT.size(); // 记录t中的不同字符的个数for (int right = 0; right < m; right++) {char sr = s.charAt(right);cntS.put(sr, cntS.getOrDefault(sr, 0) + 1);// 如果s中的字符完全匹配t中的字符if (cntT.containsKey(sr) && cntS.get(sr).intValue() == cntT.get(sr).intValue()) {formed++;}// 当s子串能覆盖t的时候收缩窗口while (formed == required) {if (right - left < ansRight - ansLeft) {ansLeft = left;ansRight = right;}// 收缩窗口char leftChar = s.charAt(left);cntS.put(leftChar, cntS.get(leftChar) - 1);if (cntT.containsKey(leftChar) && cntS.get(leftChar).intValue() < cntT.get(leftChar).intValue()) {formed--;}left++;}}return ansLeft < 0 ? "" : s.substring(ansLeft, ansRight + 1); // 左闭右开}
}
相关文章:
【LeetCode Hot100 子串】和为 k 的子数组、滑动窗口最大值、最小覆盖子串
子串 1. 和为 k 的子数组题目描述解题思路主要思路步骤 时间复杂度与空间复杂度代码实现 2. 滑动窗口最大值题目描述解题思路双端队列的原理:优化步骤: Java实现 3. 最小覆盖子串题目描述解题思路滑动窗口的基本思路:具体步骤:算法…...
某虚拟页式存储管理系统中有一个程序占8个页面,运行时访问页面的顺序是1,2,3,4,5,3,4,1,6,7,8,7,8,5。假设刚开始内存没有预装入任何页面。
某虚拟页式存储管理系统中有一个程序占8个页面,运行时访问页面的顺序是1,2,3,4,5,3,4,1,6,7,8,7,8,5。假设刚开始内存没有预装入任何页面。 (1) 如果采用LRU调度算法,该程序在得到4块内存空间时,会产生多少次缺页中断?请给出详细…...
傅里叶公式推导(三)
文章目录 周期 2L周期T 周期 2L 周期 T 2 L T2L T2L 的傅里叶变换 即 f ( t ) f ( t 2 L ) f(t) f(t2L) f(t)f(t2L) xt2 π \pi π 2 L 2L 2L 原公式 f ( x ) a 0 2 ∑ n 1 ∞ [ a n cos n x b n sin n x ] a 0 1 π ∫ − π π f ( x ) d x a n 1 π ∫…...
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_time_update函数
定义在 src\core\ngx_times.c 中 ngx_time_init 函数后面 void ngx_time_update(void) {u_char *p0, *p1, *p2, *p3, *p4;ngx_tm_t tm, gmt;time_t sec;ngx_uint_t msec;ngx_time_t *tp;struct timeval tv;if (!ngx_trylock(&ngx…...
老牌系统工具箱,现在还能打!
今天给大家分享一款超实用的电脑软硬件检测工具,虽然它是一款比较“资深”的软件,但依然非常好用,完全能满足我们的日常需求。 电脑软硬件维护检测工具 功能强大易用 这款软件非常贴心,完全不需要安装,直接打开就能用…...
mysql error1449解决方法
MySQL Error 1449 错误信息为 “The user specified as a definer (userhost) does not exist”,意思是定义者(创建存储过程、函数、触发器等数据库对象时指定的用户)在当前系统中不存在,从而导致无法正常使用这些对象。以下是针对…...
Notepad++ 中删除所有以 “pdf“ 结尾的行
Notepad 中删除所有以 “pdf” 结尾的行 操作步骤 1.打开文件: 在 Notepad 中打开你需要处理的文本文件。 2.打开查找和替换对话框: 按快捷键 Ctrl F,打开“查找和替换”对话框。 3.启用正则表达式模式: 在对话框的底部…...
归并排序 和 七大算法的总结图
目录 什么是递归排序: 图解: 递归方法: 代码实现: 思路分析: 非递归方法: 思路: 代码实现: 思路分析: 什么是递归排序: 先将数据分解成诺干个序列࿰…...
嵌入式硬件篇---原码、补码、反码
文章目录 前言简介八进制原码、反码、补码1. 原码规则示例问题 2. 反码规则示例问题 3. 补码规则示例优点 4. 补码的运算5. 总结 十六进制原码、反码、补码1. 十六进制的基本概念2. 十六进制的原码规则示例 3. 十六进制的反码规则示例 4. 十六进制的补码规则示例 5. 十六进制补…...
评估多智能体协作网络(MACNET)的性能:COT和AUTOGPT基线方法
评估多智能体协作网络(MACNET)的性能 方法选择:选择COT(思维链,Chain of Thought)、AUTOGPT等作为基线方法。 COT是一种通过在推理过程中生成中间推理步骤,来增强语言模型推理能力的方法,能让模型更好地处理复杂问题,比如在数学问题求解中,展示解题步骤。 AUTOGPT则是…...
洛谷题目: P2398 GCD SUM 题解 (本题较难,省选-难度)
题目传送门: P2398 GCD SUM - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 前言: 本题涉及到 欧拉函数,素数判断,质数,筛法 ,三大知识点,相对来说还是比较难的。 本题要求我们计算 …...
kubernetes-cni 框架源码分析
深入探索 Kubernetes 网络模型和网络通信 Kubernetes 定义了一种简单、一致的网络模型,基于扁平网络结构的设计,无需将主机端口与网络端口进行映射便可以进行高效地通讯,也无需其他组件进行转发。该模型也使应用程序很容易从虚拟机或者主机物…...
AI Agent有哪些痛点问题
AI Agent有哪些痛点问题 目录 AI Agent有哪些痛点问题AI Agent领域有哪些知名的论文缺乏一个将智能多智能体技术和在真实环境中学习的两个适用流程结合起来的统一框架LLM的代理在量化和客观评估方面存在挑战自主代理在动态环境中学习、推理和驾驭不确定性存在挑战AI Agent领域有…...
使用Java爬虫获取京东JD.item_sku API接口数据
在电商领域,商品的SKU(Stock Keeping Unit)信息是运营和管理的关键数据。SKU信息包括商品的规格、价格、库存等,对于商家的库存管理、定价策略和市场分析至关重要。京东作为国内领先的电商平台,提供了丰富的API接口&am…...
华为云+硅基流动使用Chatbox接入DeepSeek-R1满血版671B
华为云硅基流动使用Chatbox接入DeepSeek-R1满血版671B 硅基流动 1.1 注册登录 1.2 实名认证 1.3 创建API密钥 1.4 客户端工具 OllamaChatboxCherry StudioAnythingLLM 资源包下载: AI聊天本地客户端 接入Chatbox客户端 点击设置 选择SiliconFloW API 粘贴1.3创…...
平方数列与立方数列求和的数学推导
先上结论: 平方数列求和公式为: S 2 ( n ) n ( n 1 ) ( 2 n 1 ) 6 S_2(n) \frac{n(n1)(2n1)}{6} S2(n)6n(n1)(2n1) 立方数列求和公式为: S 3 ( n ) ( n ( n 1 ) 2 ) 2 S_3(n) \left( \frac{n(n1)}{2} \right)^2 S3(n)(2n(n1)…...
Java中的synchronized关键字与锁升级机制
在多线程编程中,线程同步是确保程序正确执行的关键。当多个线程同时访问共享资源时,如果不进行同步管理,可能会导致数据不一致的问题。为了避免这些问题,Java 提供了多种同步机制,其中最常见的就是 synchronized 关键字…...
告别传统校准!GNSS模拟器在计量行业的应用
随着GNSS技术的不断进步,各类设备广泛采用该技术实现高精度定位,并推动了其在众多领域的广泛应用。对于关键行业如汽车制造和基础设施,设备的可用性和可靠性被视为基本准则,GNSS作为提供“绝对位置”信息的关键传感器,…...
数据结构结尾
1.二叉树的分类 搜索二叉树,平衡二叉树,红黑树,B树,B树 2.Makefile文件管理 注意: 时间戳:根据时间戳,只编译发生修改后的文件 算法: 算法有如上五个要求。 算法的时间复杂度&am…...
【golang】量化开发学习(一)
均值回归策略简介 均值回归(Mean Reversion)假设价格会围绕均值波动,当价格偏离均值一定程度后,会回归到均值。 基本逻辑: 计算一段时间内的移动均值(如 20 天均线)。当当前价格高于均值一定比…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
