模板:
- 初始左指针(left)和右指针(right)都指向0
- 右指针不断向右移动
- 当满足窗口大小时候,进行相应的处理,这个和过程中需要left向右移动
def sliding_window(window_size, array):
left, right = 0, 0
res = 0
while right < len(array):
if condition:
pass
while n > window_size or n == window_size:
...
left += 1 # 右移left
res = max(res, xxx)
...
res = max(res, xxx)
right += 1 # 右移right
return res
1、无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
此题不限制窗口大小,窗口不含有重复字符就可以
def lengthOfLongestSubstring(self, s):
left, right = -1, 0 # left = -1 是为了第一个字符计算方便
maps = {}
max_len = 0
while right < len(s):
# 初始或者当前字符在窗口左侧出现过
if maps.get(s[right]) is None or maps.get(s[right]) < left:
max_len = max(max_len, right-left)
else:
left = maps.get(s[right])
maps[s[right]] = right
right += 1
return max_len
2、替换后的最长重复字符
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-repeating-character-replacement/
此题不限制窗口大小,窗口能替换成一样的字符就可以
def characterReplacement(self, s, k):
if not s:
return 0
letter_num = {}
left, right = 0, 0
res = 0
while right < len(s):
letter_num[s[right]] = letter_num.get(s[right], 0) + 1 # 统计字符个数
max_letter = max(letter_num, key=letter_num.get) # 计算字符数量最多的字符
while right - left + 1 - letter_num[max_letter] > k: # 当替换次数大于阈值k,进入循环不断将窗口左端字符移除,直到满足要求
letter_num[s[left]] -= 1
left += 1
max_letter = max(letter_num, key=letter_num.get) # 此时要不断记录最大的窗口
res = max(res, right - left + 1)
right += 1
return res
3、滑动窗口中位数
给出一个数组 nums,有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-median/
排序数组的插入和查找用了个模块bisect ,用法参考:https://www.cnblogs.com/skydesign/archive/2011/09/02/2163592.html
def medianSlidingWindow(self, nums, k):
result = []
if not nums or not k:
return result
# 计算中位数
def calculate_median(window, k):
if k % 2 == 1:
median = window[k//2]
else:
median = (window[k//2 - 1] + window[k//2])/2.0
return median
n = len(nums)
left, right = 0, k
# 计算第一个窗口的中位数
window = sorted(nums[left:right])
result.append(calculate_median(window, k))
while right < n:
bisect.insort(window, nums[right]) # 将当前数插入到窗口中
# 删除最左边的数
index = bisect.bisect_left(window, nums[left])
del window[index]
result.append(calculate_median(window, k)) # 重新计算中位数并记录
left += 1
right += 1
return result
4、字符串的排列
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-in-string/
def checkInclusion(self, s1, s2):
if s1 in s2:
return True
if len(s1)>len(s2):
return False
counter = collections.Counter(s1) # 统计s1字符
left, right = 0, 0
temp_counter = dict(counter)
while right < len(s2):
if s2[right] not in temp_counter: # 若s1中不包含,则重新统计,恢复counter
right += 1
left = right
temp_counter = dict(counter)
continue
else:
while temp_counter[s2[right]] == 0: # 窗口中right字符已经够数了,不断移除左字符,直到能放得下右字符
temp_counter[s2[left]] += 1
left += 1
temp_counter[s2[right]] -= 1
if temp_counter[s2[right]] > 0: # 窗口中字符数量不满足s1,继续移动,,此模块为了提高效率(针对下文的all)
right += 1
continue
if all(v == 0 for v in temp_counter.values()): # 满足则返回退出
return True
right += 1
return False
5、K 个不同整数的子数组
给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subarrays-with-k-different-integers/
重复元素的数量要算在内
def subarraysWithKDistinct(self, A, K):
if not A or K == 0:
return 0
maps = collections.defaultdict(int)
n = len(A)
left, res, cnt, tmp = 0, 0, 0, 1
for right in range(n):
maps[A[right]] += 1
if maps[A[right]] == 1: # 说明新来了一个字符,窗口不同字符数量+1
cnt += 1
while cnt > K: # 不满足要求,移动左指针
tmp = 1
cnt -= 1
maps[A[left]] -= 1
left += 1
# 移动左指针的条件,直到满足条件,这里需要注意的是左指针移动一次,算新来一个数!这个时候才tmp++
while maps[A[left]] > 1: # 大于1说明有重复元素,留下一个就可以,tmp记录有重复字符时候的子数组数量
tmp += 1
maps[A[left]] -= 1
left += 1
if cnt == K:
res += tmp
return res
6、滑动窗口最大值
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-maximum/
应用单调双边队列(递减),保证最大值在第一个,实现双边队列用 from collections import deque
def maxSlidingWindow(self, nums, k):
# 单调双边队列
if not nums or not k:
return []
res = []
queue = []
for i in range(len(nums)):
while queue and nums[i] > nums[queue[-1]]:
queue.pop()
queue.append(i)
if i >= k-1:
res.append(nums[queue[0]])
if i - queue[0] + 1 == k:
queue.pop(0)
return res
7、最长湍流子数组
当 A 的子数组 A[i], A[i+1], ..., A[j] 满足下列条件时,我们称其为湍流子数组:
若 i <= k < j,当 k 为奇数时, A[k] > A[k+1],且当 k 为偶数时,A[k] < A[k+1];
或 若 i <= k < j,当 k 为偶数时,A[k] > A[k+1] ,且当 k 为奇数时, A[k] < A[k+1]。
也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组。
返回 A 的最大湍流子数组的长度。
链接:https://leetcode-cn.com/problems/longest-turbulent-subarray/
def maxTurbulenceSize(self, A):
"""
:type A: List[int]
:rtype: int
"""
if len(A) < 2:
return len(A)
max_len = 1
left, right = 0, 2
while right < len(A):
if A[right - 1] == A[right]:
left = right
right += 1
elif A[right - 2] > A[right - 1] < A[right] or A[right - 2] < A[right - 1] > A[right]:
max_len = max(max_len, right - left + 1)
right += 1
elif A[right - 1] < A[right] or A[right - 1] > A[right]:
max_len = max(max_len, 2)
left = right - 1
right += 1
return max_len