一、滑动窗口概述
滑动窗口,顾名思义就是一个可以滑动的窗口,它可以用来解决一些数组/字符串的子元素问题。
滑动窗口的核心思想是,定义一些指针和索引来表示一些位置,移动窗口,更新状态,并且在不遗漏任何位置的前提下,找到我们需要的答案。
那么,程序员在使用滑动窗口算法时,需要如何思考呢?
首先,一定是定义一个窗口,这个窗口通常是一个连续的子区间,然后用变量 left
和 right
表示窗口的左右边界,再根据问题的需要定义一些其它变量。然后,窗口开始向右滑动,当窗口右端点右移时,也就是 right += 1
时,窗口中多了一个右边的元素,窗口的和会发生什么变化?需要减少还是增加?这部分问题需要我们自己去思考。
二、常用滑动窗口算法题目及解法
1. 最小覆盖子串
题目描述: 给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。
解法: 使用双指针和哈希表。首先使用哈希表记录下字符串 T 中每个字符出现的次数,然后使用双指针 left
和 right
来表示窗口的左右边界。当窗口中包含所有字符时,取最小子串,然后左指针右移,不断更新最小字符串。当窗口中不包含所有字符时,右指针右移。
class Solution:
def minWindow(self, s: str, t: str) -> str:
if not s or not t:
return ""
left, right = 0, 0
cnt = len(t)
need = {}
for i in t:
need[i] = need.get(i, 0) + 1
res = ""
min_len = float("inf")
while right < len(s):
if s[right] in need:
if need[s[right]] > 0:
cnt -= 1
need[s[right]] -= 1
right += 1
while cnt == 0:
if (right - left) < min_len:
min_len = right - left
res = s[left:right]
if s[left] in need:
if need[s[left]] == 0:
cnt += 1
need[s[left]] += 1
left += 1
return res
2. 求和等于K的最长子数组
题目描述: 给定一个整数数组 nums
和一个目标值 k
,请找到数组中和等于 k
的最长子数组长度。如果不存在任意一个符合条件的子数组,则返回 0
。
解法: 使用前缀和加哈希表。首先,定义一个哈希表,存储每个前缀和出现的最早下标。然后,定义一个变量 sum
为前缀和,当 sum - k
在哈希表中存在时,说明从 i
到 j
存在一个子数组和等于 k
,将其长度与之前计算的最大长度进行比较,更新答案。
class Solution:
def maxSubArrayLen(self, nums: List[int], k: int) -> int:
if not nums:
return 0
res = 0
prefix_sum = 0
has_dict = {0:-1}
for i in range(len(nums)):
prefix_sum += nums[i]
if prefix_sum - k in has_dict:
res = max(res, i - has_dict[prefix_sum - k])
if prefix_sum not in has_dict:
has_dict[prefix_sum] = i
return res
三、滑动窗口要点总结
- 滑动窗口算法(双指针算法)是一种固定长度的窗口在数组上滑动,通过双指针(数组坐标)对数组一个个进行滑动统计,并得出问题的解。
- 滑动窗口的时间复杂度通常为
O(n)
,比一般的暴力解法要高效许多。 - 当想要在一个数组或是一个字符串中找到一个子区间,滑动窗口算法应该是一个不错的选择。
- 在使用滑动窗口算法的过程中,一定要注意边界问题,并且稍加思考时,可以使用哈希表等数据结构优化算法。
- 掌握了滑动窗口算法的思想和模板,我们可以针对具体问题进行适当的修改和调整,解决更多的问题。