模板:
- 初始左指針(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 []
# push進新元素,滿足單調性
def push(queue, nums, i):
while queue and nums[i] > nums[queue[-1]]:
queue.pop()
queue.append(i)
from collections import deque
res = []
queue = deque()
for i in range(k-1): # 將比窗口小1個初始元素放進隊列
push(queue, nums, i)
for i in range(k-1, len(nums)):
push(queue, nums, i) # 不斷放進新元素
res.append(nums[queue[0]]) # 讀最大值保存
if k == i-queue[0]+1: # 窗口裝滿了,移除最左的
queue.popleft()
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