算法介绍
滑动窗口法是在给定特定窗口大小的数组或字符串上执行要求的操作,该算法可以将部分问题中的嵌套循环转变为单循环,具有较小的时间复杂度O(n2)→O(n)。
算法框架
- 滑动:窗口往某个方向移动的。
- 窗口:窗口大小可以是固定的,也可以是变化的,可以不断扩容直到满足一定的条件,也可以不断缩小直到找到一个满足条件的最小窗口。
算法思路
- 定义左右指针,初始化left = right = 0,把索引闭区间[left, right]称为一个窗口。
- 不断增加right指针扩大窗口[left, right],直到窗口中的部分符合要求(寻找可行解)。
- 停止增加right指针,然后不断增加left指针缩小窗口[left, right],直到窗口中的部分不再符合要求,于此同时需要不断更新结果(优化可行解,寻找最优解)。
- 重复第2步和第3步,直到right指针到达字符串或者数组的尽头。
算法模板
# 窗口大小不固定
left, right = 0, 0
res = ??? # 根据实际需要设置
while right < n:
window.add(right指针对应的元素)
right += 1
while window符合要求:
检查是否需要更新res
window.remove(left指针对应的元素)
low += 1
return res
# 窗口大小固定
right = 0
res = ??? # 根据实际需要设置
while right < n:
window.add(right指针对应的元素)
right += 1
if right >= k: # 窗口达到最大值
检查是否需要更新res
window.remove(right-k对应的元素)
return res
# 巧用哈希表记录
left, right = 0, 0
res = ??? # 根据实际需要设置
hashTable = set()/defaultdict(int)
while right < n:
更新hashTable # window.add(right对应的元素)
检查是否需要更新res
while window符合要求:
更新hashTable # window.remove(left对应的元素)
检查是否需要更新res
right += 1
return res
算法应用
- 窗口大小不固定
LeetCode209.长度最小的子数组
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n = len(nums)
res = n+1
low, high = 0, 0
count = 0
while high < n:
count += nums[high]
while count >= target:
res = min(res, high-low+1)
count -= nums[low]
low += 1
high += 1
return 0 if res == n+1 else res
- 窗口大小固定
LeetCode219.存在重复元素II
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
n = len(nums)
sonNums = set()
for high in range(n):
if high > k:
sonNums.remove(nums[high-k-1])
if nums[high] in sonNums:
return True
sonNums.add(nums[high])
return False
- 巧借哈希表记录
LeetCode187.重复的DNA序列
class Solution:
def findRepeatedDnaSequences(self, s: str) -> List[str]:
n = len(s)
if n <= 10:
return []
res = []
hashTable = defaultdict(int)
high = 9
while high < n:
ason = s[high-9:high+1]
hashTable[ason] += 1
if hashTable[ason] == 2:
res.append(ason)
high += 1
return res