数组和链表代表着计算机最基本的两种存储形式:顺序存储和链式存储,所以他俩可以算是最基本的数据结构。数组是一种基础数据结构,可以用来处理常见的排序和二分搜索问题,典型的处理技巧包括双指针、滑动窗口等,数组是数据结构中的基本模块之一。因为字符串是由字符数组形成的,所以二者是相似的。

1 滑动窗口

1.1 定义

在计算机网络里经常用到滑动窗口协议(Sliding Window Protocol),该协议是 TCP协议 的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认。因此该协议可以加速数据的传输,提高网络吞吐量。

滑动窗口算法其实和这个是一样的,只是用的地方场景不一样,可以根据需要调整窗口的大小,有时也可以是固定窗口大小。

滑动窗口使用双指针解决问题,所以一般也叫双指针算法,因为两个指针间形成一个窗口。双指针也并不局限在数组问题,像链表场景的 “快慢指针” 也属于双指针的场景,其快慢指针滑动过程中本身就会产生一个窗口,比如当窗口收缩到某种程度,可以得到一些结论。

什么情况适合用滑动窗口算法呢?

  • 需要输出或比较的结果在原数据结构中是连续排列的,特别是数组或链表问题;
  • 每次窗口滑动时,只需观察窗口两端元素的变化,无论窗口多长,每次只操作两个头尾元素,当用到的窗口比较长时,可以显著减少操作次数;
  • 窗口内元素的整体性比较强,窗口滑动可以只通过操作头尾两个位置的变化实现,但对比结果时往往要用到窗口中所有元素。

滑动窗口算法常用于字符串匹配问题和子数组问题。

滑动窗口算法在一个特定大小的字符串或数组上进行操作,而不在整个字符串和数组上操作,这样就降低了问题的复杂度,从而也达到降低了循环的嵌套深度。其实这里就可以看出来滑动窗口主要应用在数组和字符串上。

1.2 滑动窗口法的大体框架

在介绍滑动窗口的框架时候,大家先从字面理解下:

  • 滑动:说明这个窗口是移动的,也就是移动是按照一定方向来的。
  • 窗口:窗口大小并不是固定的,可以不断扩容直到满足一定的条件;也可以不断缩小,直到找到一个满足条件的最小窗口;当然也可以是固定大小。

为了便于理解,这里采用的是字符串来讲解。但是对于数组其实也是一样的。滑动窗口算法的思路是这样:

  1. 我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」。
  2. 我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(包含了 T 中的所有字符)。
  3. 此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。
  4. 重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。

这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动。

1.3 滑动窗口模板

滑窗模板 Python 伪代码

class Solution:def problemName(self, s: str) -> int:# Step 1: 定义需要维护的变量们 (对于滑动窗口类题目,这些变量通常是最小长度,最大长度,或者哈希表)x, y = ..., ...# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end in range(len(s)):# Step 3: 更新需要维护的变量, 有的变量需要一个if语句来维护 (比如最大最小长度)x = new_xif condition:y = new_y'''------------- 下面是两种情况,读者请根据题意二选1 -------------'''# Step 4 - 情况1# 如果题目的窗口长度固定:用一个if语句判断一下当前窗口长度是否达到了限定长度 # 如果达到了,窗口左指针前移一个单位,从而保证下一次右指针右移时,窗口长度保持不变, # 左指针移动之前, 先更新Step 1定义的(部分或所有)维护变量 if 窗口长度达到了限定长度:# 更新 (部分或所有) 维护变量 # 窗口左指针前移一个单位保证下一次右指针右移时窗口长度保持不变# Step 4 - 情况2# 如果题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题# 如果当前窗口不合法时, 用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法# 在左指针移动之前更新Step 1定义的(部分或所有)维护变量 while 不合法:# 更新 (部分或所有) 维护变量 # 不断移动窗口左指针直到窗口再次合法# Step 5: 返回答案return ...

滑动窗口中用到了左右两个指针,它们移动的思路是:以右指针(end)作为驱动,拖着左指针(start)向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。


2 常见题型

2.1 题库列表

  • 643. 子数组最大平均数 I

  • 3. 无重复字符的最长子串

  • 159. 至多包含两个不同字符的最长子串

  • 209. 长度最小的子数组

  • 1695. 删除子数组的最大得分

  • 438. 找到字符串中所有字母异位词

  • 567. 字符串的排列

  • 487. 最大连续1的个数 II

  • 1004. 最大连续1的个数 III

  • 1208. 尽可能使字符串相等

  • 1052. 爱生气的书店老板

  • 1151. 最少交换次数来组合所有的1 Π

  • 76. 最小覆盖子串

  • 239. 滑动窗口最大值

2.2 真题演练

643. 子数组最大平均数 I
        题目描述:给你一个由 n 个元素组成的整数数组 nums 和一个整数 k。请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。

class Solution:def findMaxAverage(self, nums: List[int], k: int) -> float:# Step 1# 定义需要维护的变量# 本题求最大平均值 (其实就是求最大和),所以需要定义sum_sub_array, 同时定义一个max_value (初始值为负无穷)sum_sub_array, max_value = 0, float('-inf')# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, num in enumerate(nums):# Step 3: 更新需要维护的变量 (sum_sub_array, max_value), 不断把当前值积累到sum_sub_array上sum_sub_array += numif end - start + 1 == k:max_value = max(max_value, sum_sub_array)# Step 4# 根据题意可知窗口长度固定,所以用if# 窗口首指针前移一个单位保证窗口长度固定, 同时提前更新需要维护的变量 (sum_sub_array)if end >= k - 1:sum_sub_array -= nums[start]start += 1# Step 5: 返回答案return max_value/k

3. 无重复字符的最长子串

题目描述:给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

class Solution:def lengthOfLongestSubstring(self, s: str) -> int:# Step 1: 定义需要维护的变量, 本题求最大长度,所以需要定义max_len, 该题又涉及去重,因此还需要一个哈希表max_len, hash_map = 0, {}# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(s):# Step 3# 更新需要维护的变量 (max_len, hashmap)# i.e. 把窗口末端元素加入哈希表,使其频率加1,并且更新最大长度hash_map[tail] = hash_map.get(tail, 0) + 1if len(hash_map) == end - start + 1:max_len = max(max_len, end - start + 1)# Step 4: # 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题# 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法# 当窗口长度大于哈希表长度时候 (说明存在重复元素),窗口不合法# 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hashmap)while end - start + 1 > len(hash_map):head = s[start]hash_map[head] -= 1if hash_map[head] == 0:del hash_map[head]start += 1# Step 5: 返回答案 (最大长度)return max_len

159. 至多包含两个不同字符的最长子串

题目描述:给定一个字符串 s ,找出 至多 包含两个不同字符的最长子串 t 。

class Solution:def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:# Step 1: # 定义需要维护的变量, 本题求最大长度,所以需要定义max_len,# 该题又涉及计算不重复元素个数,因此还需要一个哈希表max_len, hashmap = 0, {}# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(s):# Step 3# 更新需要维护的变量 (max_len, hashmap)# 首先,把当前元素的计数加一# 一旦哈希表长度小于等于2(之多包含2个不同元素),尝试更新最大长度hashmap[tail] = hashmap.get(tail, 0) + 1if len(hashmap) <= 2:max_len = max(max_len, end - start + 1)# Step 4: # 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题# 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法# 哈希表长度大于2的时候 (说明存在至少3个重复元素),窗口不合法# 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hashmap)while len(hashmap) > 2:head = s[start]hashmap[head] -= 1if hashmap[head] == 0:del hashmap[head]start += 1# Step 5: 返回答案 (最大长度)return max_len

209. 长度最小的子数组

题目描述:给定一个含有 n 个正整数的数组和一个正整数 target。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [ n u m s l , n u m s l + 1 , . . . , n u m s r − 1 , n u m s r ] [nums_l, nums_{l+1}, ..., nums_{r-1}, nums_r] [numsl​,numsl+1​,...,numsr−1​,numsr​],并返回其长度。如果不存在符合条件的子数组,返回 0。

class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:# Step 1: 定义需要维护的变量, 本题求最小长度,所以需要定义min_len, 本题又涉及求和,因此还需要一个sum变量min_len, sum_sub_array = math.inf, 0# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, num in enumerate(nums):# Step 3: 更新需要维护的变量 (min_len, sum_sub_array)sum_sub_array += num# 这一段可以删除,因为下面的while已经handle了这一块儿逻辑,不过写在这也没影响if sum_sub_array >= target:min_len = min(min_len, end - start + 1)# Step 4# 这一题这里稍微有一点特别: sum_sub_array >= target其实是合法的,但由于我们要求的是最小长度,# 所以当sum_sub_array已经大于target的时候继续移动右指针没有意义,因此还是需要移动左指针慢慢逼近答案# 由于左指针的移动可能影响min_len和sum_sub_array的值,因此需要在移动前将它们更新while sum_sub_array >= target:min_len = min(min_len, end - start + 1)sum_sub_array -= nums[start]start += 1# Step 5:返回答案 (最小长度)if min_len == math.inf:return 0return min_len

1695. 删除子数组的最大得分

题目描述:给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组。删除子数组的 得分 就是子数组各元素之 和。返回 只删除一个 子数组可获得的 最大得分。如果数组 b 是数组 a 的一个连续子序列,即如果它等于 a[l],a[l+1],…,a[r] ,那么它就是 a 的一个子数组。

class Solution:def maximumUniqueSubarray(self, nums: List[int]) -> int:# Step 1# 定义需要维护的变量, 本题最大得分,所以需要定义当前得分sum_sub_array和最大得分max_sum# 本题又涉及去重 (题目规定子数组不能有重复),因此还需要一个哈希表sum_sub_array, max_sum, hashmap = 0, 0, {}# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(nums):# Step 3# 更新需要维护的变量 (sum_sub_array, hashmap)# sum和hashmap需要更新就不说了,max_sum当且仅当哈希表里面没有重复元素时 (end - start + 1 == len(hashmap)) 更新sum_sub_array += tailhashmap[tail] = hashmap.get(tail, 0) + 1if end - start + 1 == len(hashmap):max_sum = max(max_sum, sum_sub_array)# Step 4# 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题# 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法# 哈希表里面有重复元素时 (end - start + 1 > len(hashmap)) 窗口不合法# 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hashmap, sum_sub_array)while end - start + 1 > len(hashmap):head = nums[start]hashmap[head] -= 1if hashmap[head] == 0:del hashmap[head]sum_sub_array -= nums[start]start += 1# Step 5: 返回答案return max_sum

438. 找到字符串中所有字母异位词

题目描述:给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:# Step 1: # 定义需要维护的变量# 本文需要对比两组字符串是否为异位词,所以用哈希表 (abc和bac是异位词是因为他们对应的哈希表相等)# 同时我们需要找到所有合法解,所以还需要一个ans数组ans, hashmap_s = [], {}# Step 1.1: 同时把p的哈希表也建立了 (这个哈希表不需要维护,为定值)hashmap_p = Counter(p)# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(s):# Step 3: 更新需要维护的变量 (hashmap), 如果hashmap == hashmap_p,代表找到了一个解,加入到anshashmap_s[tail] = hashmap_s.get(tail, 0) + 1if hashmap_s == hashmap_p:ans.append(start)# Step 4 # 根据题意可知窗口长度固定,所以用if# 窗口左指针前移一个单位保证窗口长度固定, 同时提前更新需要维护的变量 (hashmap)if end >= len(p) - 1:head = s[start]hashmap_s[head] -= 1if hashmap_s[head] == 0:del hashmap_s[head]start += 1# Step 5: 返回答案return ans

567. 字符串的排列

题目描述:给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。

class Solution:def checkInclusion(self, s1: str, s2: str) -> bool:# Step 1# 定义需要维护的变量# 因为和排列相关 (元素相同,顺序可以不同),使用哈希表hash_map_s2 = {}# Step 1.1: 同时建立s1的哈希表 (这个哈希表不需要维护,为定值)hash_map_s1 = Counter(s1)# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(s2):# Step 3: 更新需要维护的变量 (hash_map_s2), 如果hash_map_s1 == hash_map_s2,代表s2包含s1的排列,直接returnhash_map_s2[tail] = hash_map_s2.get(tail, 0) + 1if hash_map_s1 == hash_map_s2:return True# Step 4: # 根据题意可知窗口长度固定,所以用if# 窗口左指针前移一个单位保证窗口长度固定, 同时提前更新需要维护的变量 (hash_map_s2)if end >= len(s1) - 1:head = s2[start]hash_map_s2[head] -= 1if hash_map_s2[head] == 0:del hash_map_s2[head]start += 1# Step 5: 没有在s2中找到s1的排列,返回Falsereturn False

487. 最大连续1的个数 II

题目描述:给定一个二进制数组,你可以最多将 1 个 0 翻转为 1,找出其中最大连续 1 的个数。

class Solution:def findMaxConsecutiveOnes(self, nums: List[int]) -> int:# Step 1# 定义需要维护的变量# 因为是求最大长度,所以有max_len,又同时涉及计数 (0的个数不能超过1个),所以还要一个哈希表max_len, hash_map = 0, {}# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(nums):# Step 3: 更新需要维护的变量 (hash_map, max_len)hash_map[tail] = hash_map.get(tail, 0) + 1if hash_map.get(0, 0) <= 1:max_len = max(max_len, end - start + 1)# Step 4# 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题# 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法# 当hash_map里面0的个数大于1的时候,窗口不合法# 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hash_map)while hash_map.get(0, 0) > 1:head = nums[start]hash_map[head] -= 1start += 1# Step 5: 返回答案 (最大长度)return max_len

1004. 最大连续1的个数 III
        题目描述:给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。

class Solution:def longestOnes(self, nums: List[int], k: int) -> int:max_len, start = 0, 0hash_map = {}for end, tail in enumerate(nums):hash_map[tail] = hash_map.get(tail, 0) + 1if hash_map.get(0, 0) <= k:     # 相比较于上一题,只需要把1改成kmax_len = max(max_len, end-start+1)while hash_map.get(0, 0) > k:head = nums[start]hash_map[head] -= 1start += 1return max_len

1208. 尽可能使字符串相等
        题目描述:给你两个长度相同的字符串,s 和 t。将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。

class Solution:def equalSubstring(self, s: str, t: str, max_cost: int) -> int:# Step 1: 定义需要维护的变量# 因为是求最大长度,所以有max_len,又同时涉及计算开销 (和求和一个道理), 所以还要一个cur_costcur_cost, max_len = 0, 0# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end in range(len(t)):# Step 3# 更新需要维护的变量 (cur_cost)# 每一对字符的order差值就是当前时间点的开销,直接累积在cur_cost上即可# cur_cost只要不超过最大开销,就更新max_lencur_cost += abs(ord(s[end]) - ord(t[end]))if cur_cost <= max_cost:max_len = max(max_len, end - start + 1)# Step 4# 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题# 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法# 当cur_cost大于最大开销时候,窗口不合法# 所以需要不断移动窗口左指针直到窗口再次合法 (cur_cost <= max_cost)while cur_cost > max_cost:cur_cost -= abs(ord(s[start])-  ord(t[start]))start += 1# Step 5: 返回答案 (最大长度)return max_len

1052. 爱生气的书店老板

题目描述

class Solution:def maxSatisfied(self, customers: List[int], grumpy: List[int], minutes: int) -> int:# Step 1# 定义需要维护的变量,# 因为涉及求和所以定义sum_sub_array和max_sum, 同时需要知道老板什么时候'发动技能',再定义一个max_startsum_sub_array, max_sum, max_start = 0, 0, 0# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(customers):# Step 3# 更新需要维护的变量 (sum_sub_array)# 注意:这里只要当老板在当前时间点会发脾气的时候才维护# sum_sub_array就不说了,和前面N道题的维护方法一样,新多出来的max_start也就是记录一样时间点而已,没什么fancy的if grumpy[end] == 1:sum_sub_array += tailif sum_sub_array > max_sum:max_sum = sum_sub_arraymax_start = start# Step 4# 根据题意可知窗口长度固定 (老板技能持续时间固定),所以用if# 窗口左指针前移一个单位保证窗口长度固定, 同时提前更新需要维护的变量 (sum_sub_array, max_avg)if end >= minutes - 1:if grumpy[start]:sum_sub_array -= customers[start]start += 1# 这里对比其他题目多了一小步: 在找到老板发动技能的最大收益时间点(max_start)后# 需要把受技能影响时间段中的grumpy全部置0 - 代表老板成功压制了自己的怒火grumpy[max_start:max_start+minutes] = [0] * minutes# Step 5: 再遍历一遍数组求customer总数量并且返回结果   res = 0for customer, grum in zip(customers, grumpy):if grum == 0:res += customerreturn res

1423. 可获得的最大点数

题目描述

class Solution:# 这题相比前面的题目加了一丢丢小的变通: 题目要求首尾串最大点数,其实就是求非首尾串的连续序列的最小点数def maxScore(self, cardPoints: List[int], k: int) -> int:# Step 1# 定义需要维护的变量,因为涉及求和所以定义sum_sub_array和min_sumsum_sub_array, min_sum = 0, float('inf')# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end, tail in enumerate(cardPoints):# Step 3# 更新需要维护的变量 (sum_sub_array)sum_sub_array += tailif len(cardPoints)-k == end - start + 1:min_sum = min(min_sum, sum_sub_array)# Step 4# 根据题意可知窗口长度固定,所以用if# 窗口左指针前移一个单位保证窗口长度固定, 同时提前更新需要维护的变量 (min_sum, sum_sub_array)if end >= len(cardPoints) - k - 1:sum_sub_array -= cardPoints[start]start += 1# Step 5: 返回答案 (总点数减去非首尾串的连续序列的最小点数就可以得到首尾串的最大点数)return sum(cardPoints) - min_sum if min_sum != inf else sum(cardPoints)

1151. 最少交换次数来组合所有的1 Π

题目描述:给出一个二进制数组data,你需要通过交换位置,将数组中任何位置上的1组合到一起,并返回所有可能中所需最少的交换次数。

class Solution:def minSwaps(self, data: List[int]) -> int:# 先数出一共有多少个1,输出来的个数就是窗口的长度num_ones = data.count(1)# Step 1# 定义需要维护的变量,求最小swap次数其实就是求窗口中0个数的最小值,因此定义num_zeros, min_num_zerosnum_zeros, min_num_zeros = 0, math.inf# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口start = 0for end in range(len(data)):# Step 3# 更新需要维护的变量 (num_zeros, min_num_zeros)if data[end] == 0:num_zeros += 1if end - start + 1 == num_ones:min_num_zeros = min(min_num_zeros, num_zeros)# Step 4# 根据题意可知窗口长度固定 (数组1的总个数),所以用if# 窗口左指针前移一个单位保证窗口长度固定, 同时提前更新需要维护的变量 (num_zeros)if end >= num_ones - 1:if data[start] == 0:num_zeros -= 1start += 1# Step 5: 返回答案 (如果min_num_zeros依旧是math.inf说明数组没有1存在,不能swap,返回0即可)return min_num_zeros if min_num_zeros != math.inf else 0

76. 最小覆盖子串

题目描述:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

from collections import Counter
class Solution:def minWindow(self, s: str, t: str) -> str:need_dict = Counter(t)                      # 哈希表:记录需要匹配到的各个元素的数目need_count = len(t)                         # 记录需要匹配到的字符总数(need=0表示匹配到了)left = 0                                    # 窗口的左边界res = (0, inf)                              # 记录目标子串 s[res[0], res[1]+1] 的起始和结尾for right, tail in enumerate(s):             # 窗口右边界右移一位,窗口中增加的字符 tailif tail in need_dict:                    # 窗口新加入的字符位于t中if need_dict[tail] > 0:              # 对当前字符还有需求need_count -= 1                 # 此时新加入窗口中的 tail 对 need_count 有影响need_dict[tail] -= 1while need_count == 0:                  # 窗口左边界持续右移,need_count=0,当前窗口完全覆盖了 tif res[1] - res[0] > right - left:  # 出现了更短的字符串res = (left, right)head = s[left]                       # 窗口中要滑出的字符 headif head in need_dict:                # 刚滑出的字符 head 位于 t 中if need_dict[head] >= 0:         # 对当前字符ch还有需求,或刚好无需求(其实此时只有=0的情况)need_count += 1             # 此时滑出窗口的 head 会对 need_count 有影响need_dict[head] += 1left += 1                           # 窗口左边界+1return s[res[0]:res[1]+1] if res[1] < len(s) else ""

239. 滑动窗口最大值

题目描述

from collections import deque
class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:# 用双端队列来存储数组的下标,为什么要存下标而不是存数值?# 因为存下标可以更方便的来确定元素是否需要移出滑动窗口# 判断下标是否合法来确定是否要移出if k == 1:return numsif k == len(nums):return [max(nums)]result, queue = [], deque()             # 使用collections内置的双端队列,加快运行速度for i in range(len(nums)):# 如果当前队列最左侧存储的下标等于 i-k 的值,代表目前队列已满。# 但是新元素需要进来,所以列表最左侧的下标出队列if queue and queue[0] == i - k:           queue.popleft()while queue and nums[queue[-1]] < nums[i]:      # 对于新进入的元素,如果队列前面的数比它小,那么前面的都出队列queue.pop()queue.append(i)         # 新元素入队列if i >= k-1:            # 当前的大值加入到结果数组中result.append(nums[queue[0]])return result

滑动窗口暂时告一段落,后面在学习中持续补充,谢谢大家的鼓励和支持!


参考

  • 双指针套路总结:https://zhuanlan.zhihu.com/p/95747836
  • 数组+常见题型与解题策略:https://blog.csdn.net/qq_42647903/article/details/120594856
  • 算法与数据结构(一):滑动窗口法总结:https://blog.csdn.net/Dby_freedom/article/details/89066140
  • 滑动窗口法python模板写法:https://blog.csdn.net/weixin_44414948/article/details/113862173
  • 滑动窗口的应用:https://leetcode.cn/problems/minimum-window-substring/solutions/1503454/by-flix-1kac/
  • 秒杀12道中档题————滑动窗口:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solutions/876061/yi-ge-mo-ban-miao-sha-10dao-zhong-deng-n-sb0x/

Python数据结构与算法篇(四)-- 滑动窗口算法相关推荐

  1. 算法篇之-----滑动窗口(尺取法)

    滑动窗口(尺取法 1. 介绍 2. 滑动窗口法的大体框架 4.最小覆盖子串 5.窗口数量 6.最小值 1. 介绍 滑动窗口法,也叫尺取法(可能也不一定相等,大概就是这样 =.=),可以用来解决一些查找 ...

  2. 微服务限流及熔断一:四种限流算法(计数器算法、滑动窗口算法、令牌限流算法、漏桶限流算法)

    引言 本篇内容根据<spring cloud alibaba 微服务原理与实战>中内容摘取,希望和大家分享限流的思想,本篇不涉及代码层面的实现. 限流的目的 目的:通过限制并发访问数或者限 ...

  3. 从零开始学习VIO笔记 --- 第四讲:滑动窗口(基于滑动窗口算法的 VIO 系统:可观性和一致性)

    从零开始学习VIO笔记 --- 第四讲:滑动窗口(基于滑动窗口算法的 VIO 系统:可观性和一致性) 一. 从高斯分布到信息矩阵 1.1 高斯分布 1.2 高斯分布和协方差矩阵 1.3 信息矩阵 二. ...

  4. 算法与数据结构(一):滑动窗口法总结

    滑窗法在算法题中大量应用,其思想简洁强大,但是往往在维护左右指针时候容易出错,现总结整理如下: 1. 介绍 滑动窗口法,也叫尺取法(可能也不一定相等,大概就是这样 =.=),可以用来解决一些查找满足一 ...

  5. 列表左右箭头滑动_我写了一套框架,把滑动窗口算法变成了默写题

    读完本文,你可以去力扣拿下如下题目: 76.最小覆盖子串 567.字符串的排列 438.找到字符串中所有字母异位词 3.无重复字符的最长子串 ----------- 鉴于前文 二分搜索框架详解 的那首 ...

  6. 滑动窗口算法_有点难度,几道和「滑动窗口」有关的算法面试题

    前言科普:什么是滑动窗口算法 滑动问题包含一个滑动窗口,它是一个运行在一个大数组上的子列表,该数组是一个底层元素集合. 假设有数组 [a b c d e f g h ],一个大小为 3 的 **滑动窗 ...

  7. 面试难点!常用算法技巧之“滑动窗口”

    算法简介 滑动窗口,顾名思义,就是有一个大小可变的窗口,左右两端方向一致的向前滑动(右端固定,左端滑动:左端固定,右端滑动). 可以想象成队列,一端在push元素,另一端在pop元素,如下所示: 假设 ...

  8. 滑动窗口算法通用思想

    文章目录 一.最小覆盖子串 二.找到字符串中所有字母异位词 三.无重复字符的最长子串 最后总结 本文详解「滑动窗口」这种高级双指针技巧的算法框架,带你秒杀几道难度较大的子字符串匹配问题: 最小覆盖子串 ...

  9. 滑动窗口算法基本原理与实践

    原文作者:huansky 原文地址:滑动窗口算法基本原理与实践 目录 滑动窗口算法(Sliding Window Algorithm) 基本示例 滑动窗口法的大体框架 算法实例 1208. 尽可能使字 ...

最新文章

  1. Spark技术在京东智能供应链预测的应用——按照业务进行划分,然后利用scikit learn进行单机训练并预测...
  2. 微机原理实验1:字符串匹配程序实验
  3. centos 卸载ffmpeg_CentOS Linux 操作系统安装 FFmpeg 教程
  4. html radio 去掉圆点,html radio默认选中,去除选中
  5. java-信息安全(九)-基于DH,非对称加密,对称加密等理解HTTPS
  6. springmvc:405 request method post not supported
  7. Python学习笔记1:数据模型和特殊方法(魔术方法)
  8. linux应用--yum
  9. php中文网是什么需要框架,框架是什么?
  10. 一文了解11个常见的多变量分析方法!
  11. rust模组服没了_[怎么看rust服务器人数]rust标准服务器和模组服务
  12. Oracle查数据库某字段的本年数,上年同期数,同比
  13. 中央电视台最常用的 100 首经典背景乐曲(视频制作音乐推荐) 2009-05-12 17:31:47
  14. latex编译报错:Font ntx-Regular- tlf-ot1r at 438 not found
  15. 堡垒机AccessClient插件在mac系统下闪退的解决办法
  16. 【本人秃顶程序员】求求你别再写 bug 了,秃顶程序员告诉你避免空指针的 5 个案例!
  17. 英国金融监管机构加大力度审查违规加密货币公司
  18. lane是什么意思_lane是什么意思_lane的翻译_音标_读音_用法_例句_爱词霸在线词典...
  19. 以太网 DHCP(简介、DHCP工作原理、租期时间)
  20. linux没有manconfig文件,linux shell man命令详细介绍

热门文章

  1. 机器学习:基本概念-标签、特征、样本、模型、回归与分类
  2. 2.3递归实现斐波那契数列
  3. 无hadoop环境 部署Kylin4 迁移元数据
  4. MATLAB给数据加噪声/扰动
  5. 一个高铁线杆杆号自动识别分类系统
  6. java广西科技大学第一附属医院陪护椅管理计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
  7. Windows鼠标隐藏
  8. python file.chunks()的使用
  9. 微信小程序中使用echarts
  10. 记一次“登录Mysql数据库输入正确密码但数据库显示密码错误”问题的处理