滑动窗口与双指针
滑动窗口与双指针
灵神题单:分享丨【算法题单】滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)- 讨论 - 力扣(LeetCode)
场景:连续、遍历各情况
尚未分类
两数之和
给定一个整数数组
nums和一个整数目标值target,请你在该数组中找出 和为目标值target的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例
| |
代码
| |
what I learn
- 哈希表的查找用in方法可实现,用dict()或set()最好,用空间换时间。
- 集合、字典的 in 方法时间复杂度O(1),列表和元组的 in 时间复杂度O(n)。
删除有序数组中的重复项
26. 删除有序数组中的重复项 - 力扣(LeetCode)
给你一个 非严格递增排列 的数组
nums,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回nums中唯一元素的个数。考虑
nums的唯一元素的数量为k,你需要做以下事情确保你的题解可以被通过:更改数组
nums,使nums的前k个元素包含唯一元素,并按照它们最初在nums中出现的顺序排列。nums的其余元素与nums的大小不重要。返回
k。
示例
| |
代码
| |
what I learn
- 快慢指针,数据需要有有序性。
搜索插入位置
**(二分法)**记住就好了 T-T
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为
O(log n)的算法。示例:
| |
代码
| |
what I learn
- 二分法框架:
- 上下界up=len(nums) low=0
- 取中间(low+up)//2
- 终止条件 low==up
- 上界降 up=mid
- 下界升 low=mid+1
加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
| |
- 示例 2:
| |
代码
| |
what I learn
- 逆序索引 range(len(digits)-1,-1,-1) 从len(digits)-1开始,步长为-1(递减),到-1,左闭右开[len(digits)-1, -1)。
定长滑动窗口
- 共性在于“一进一出”
- 记录变化计算、设置、边界值处理
- 逆向、排序、分组等特殊处理
半径为 k 的子数组平均值
2090. 半径为 k 的子数组平均值 - 力扣(LeetCode)
给你一个下标从 0 开始的数组
nums,数组中有n个整数,另给你一个整数k。半径为 k 的子数组平均值 是指:
nums中一个以下标i为 中心 且 半径 为k的子数组中所有元素的平均值,即下标在i - k和i + k范围(含i - k和i + k)内所有元素的平均值。如果在下标i前或后不足k个元素,那么 半径为 k 的子数组平均值 是-1。构建并返回一个长度为
n的数组avgs,其中avgs[i]是以下标i为中心的子数组的 半径为 k 的子数组平均值 。x个元素的 平均值 是x个元素相加之和除以x,此时使用截断式 整数除法 ,即需要去掉结果的小数部分。例如,四个元素
2、3、1和5的平均值是(2 + 3 + 1 + 5) / 4 = 11 / 4 = 2.75,截断后得到2。

| |
代码
| |
what I learn
- 观察问题的本质,不要被方法带着走。
- 问题的本质:待到要处理的索引时,得出平均值放入数组。
- 方法带偏:想着边遍历边append,开头去append -1,结束了再补充-1。
- 解决的关键:初始化nums长度的 -1数组,即 [-1] * len(nums)。
几乎唯一子数组的最大和
2841. 几乎唯一子数组的最大和 - 力扣(LeetCode)
给你一个整数数组
nums和两个正整数m和k。请你返回
nums中长度为k的 几乎唯一 子数组的 最大和 ,如果不存在几乎唯一子数组,请你返回0。如果
nums的一个子数组有至少m个互不相同的元素,我们称它是 几乎唯一 子数组。子数组指的是一个数组中一段连续 非空 的元素序列。
示例
| |
代码
| |
what I learn
- sum_n = max_sum = 0
- 使用defaultdict
- 默认值为0,dif_n=defaultdict(int) 因为int()返回0(不如Counter)
- if dif_n[out] == 0: del dif_n[out] 删除其中值为0的键值对
- max_sum = max(sum_n,max_sum) 而不用 if 条件判断
- left = i-k+1 …. out=nums[left] 使代码更简洁美观
找到字符串中所有字母异位词
438. 找到字符串中所有字母异位词 - 力扣(LeetCode)
what I learn
- Counter
| |
重新安排会议得到最多空余时间 I
3439. 重新安排会议得到最多空余时间 I - 力扣(LeetCode)
- 理解题目意思,转化问题(so called 建模)为某种方法解决,此题很明显,弄出所有时间差gap,然后求最大连续子数组即可。
- 注意一些变量的值需要变化,比如这题中的“可以移动k个”,转化为k+1个连续子数组
- 注意全面性,比如gap还包括“startTime-第一个会议开始时刻” “endTime-最后一个会议结束时刻”。
使二进制字符串字符交替的最少反转次数
1888. 使二进制字符串字符交替的最少反转次数 - 力扣(LeetCode)
- 枚举法(s + s 上的滑动窗口),不要想当然得觉得 想出的算法能适应所有情况,当测试不通过时,大胆暴力,这里也就是 生成所有第一种操作而成的字符串,然后滑动遍历所有情况。
- ans=min(cnt, k - cnt, ans),在当前情况下的两种可能和全局最小 中 取最小。
查找给定哈希值的子串
2156. 查找给定哈希值的子串 - 力扣(LeetCode)
- 第一个字母的 p 的幂次是最小的,这和通常字符串哈希中的定义是相反的。所以在计算哈希值时,要倒序遍历。
- 关键在于,此题正序遍历时,要在mod m下除以p,除法需要逆元,逆元不一定存在;逆序遍历时为乘p,无需逆元。
串联所有单词的子串(次定长滑窗)
- “barfoothefoobarman”,单个“词”长度3,本因3个3个检查,于是为遍历所有情况,起始位置需为0/1,对从0/1位置开始的字符串分别滑窗。
不定长滑动窗口
- 求最大最长(越短越合法或恰好合法):内循环while使窗变小,一旦合法则无法继续循环,则此时为合法下的最长。
- 求最小最短(越长越合法或恰好合法):内循环while使窗变小,预计算一步若不合法则无法继续循环,此时为合法下的最短。
- 求子数组
无重复字符的最长子串(最长最大)
- 基础模板:左指针while调整,右指针“+1移动”,cnt为int或defaultdict(int)记录信息,ans = max(ans, right-left+1)。
- 循环内呈现:right(进入的元素)处理,while循环调整左指针和记录变量,外围答案值是否需要改变。
找到最长的半重复子字符串
2730. 找到最长的半重复子字符串 - 力扣(LeetCode)
- 稍作调整使用模板:ans的初始化(默认)值设置;记录变量的如何设置和如何变化
最高频元素的频数
- 洞悉“滑窗”特性,主要在于它的只增性质。
- 先用在框架下暴力方法分析如何解出,再优化时间复杂度。
将 x 减到 0 的最小操作数
1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)
逆向思维+滑动窗口
- 逆向思维
- 特殊情况分析
数组的最大美丽值
排序+滑动窗口
- 审题,发现排序并不影响子序列的选择;排序后分析left的移动条件即可轻易解出。
毯子覆盖的最多白色砖块数
2271. 毯子覆盖的最多白色砖块数 - 力扣(LeetCode)
排序+滑动窗口+特殊滑动单位
- 模拟发现,毯子右端在白瓷内时,覆盖面积非递减,毯子右端在非白瓷内时,覆盖面积非递增。
- 相当于每次毯子右端在白瓷右端时,计算覆盖面积看是否大于ans,于是滑动单位为一个个白瓷(一个个区间)
长度最小的子数组(最短最小)
- while total-nums[left] >= target: (“预计算一步”,即下一步不满足,现在是最短)
相向双指针
两数之和 II - 输入有序数组
167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)
- 关键在于利用“有序”进行排除,移动的指针 便是 排除“都小”或“都大”的数。
- 最终找到恰好

统计和小于目标的下标对数目
2824. 统计和小于目标的下标对数目 - 力扣(LeetCode)
- 对大于/小于的统计,都是right-left,条件不同罢了,对称性:固定左指针,右指针往左,和都小;固定右指针,左指针往右,和都大。