有些时候,我们需要获得数组或者字符串的连续子部分,这时候我们就可以考虑使用滑动窗口。nums[left,right]为滑动窗口,根据具体的要求,通过遍历的时候来改变 left和right 的位置,从而完成任务。滑动窗口主要处理连续问题,按类型主要有如下三种:
- 固定窗口大小
- 窗口大小不固定,求解最大的满足条件的窗口
- 窗口大小不固定,求解最小的满足条件的窗口
对于固定窗口,我们只需要固定初始化左右指针Ieft和right,分别表示的窗口的左右顶点,并且保证:
1、Ieft初始化为0,
2、初始化right,使得right -Ieft+1等于窗口大小,
3、同时移动Ieft和right,
4、判断窗口内的连续元素是否满足题目限定的条件。
4.1、如果满足,再判断是否需要更新最优解,如果需要则更新最优解。
4.2、如果不满足,则继续。
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
# 定义左右指针
left,right = 0,k
res =[] # 存储滑动窗口内的最大值
if len(nums) > 0:
while right <= len(nums):
maxValues = max(nums[left:right])
res.append(maxValues)
left += 1
right += 1
return res
else:
return nums
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
res = [] # 存储结果
queue = collections.deque() # 双端队列,两端快速添加(append)和弹出(pop)
for i, num in enumerate(nums):
if queue and queue[0] == i - k:
queue.popleft()
while queue and nums[queue[-1]] < num:
queue.pop()
queue.append(i)
if i >= k - 1:
res.append(nums[queue[0]])
return res
对于可变窗口,我们同样固定初始化左右指针,分别表示的窗口的左右顶点。后面有所不同,我们需要保证:
1、Ieft和right都初始化为0
2、right指针移动一步
3、判断窗口内的连续元素是否满足题目限定的条件
3.1如果满足,再判断是否需要更新最优解,如果需要则更新最优解。并尝试通过移动Ieft指针缩小窗口大小。循环执行3.1
3.2如果不满足,则继续。
给定一个含有 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
思路:
1、开始right向右滑动,使和变大。
2、当恰好>=s时,记录滑窗所包括的子数组长度res,若res 已有数值,需判断新值是否小于旧值,若是,更新res; left向右滑动。
3、判断是否仍>=s,若是,重复步骤2,3。若否,转步骤1。直到右边框到达最右边
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 初始化
left = 0
sums = 0
res = float('inf') # 无穷大
# 右指针移动
for right in range(len(nums)):
sums += nums[right]
while sums >= target:
# 判断新值是否小于旧值,如果是则更新
if right - left + 1 < res:
res = right - left + 1
# 左指针向右移动
sums -= nums[left]
left += 1
# 循环结束条件:sums
if res == float('inf'):
return 0
else:
return res
给定一个正整数数组 nums。
找出该数组内乘积小于 k 的连续的子数组的个数。
示例:
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
思路:
步骤1:当left <= right且滑动窗口内的乘积小于k时,我们可以知道[Ieft,right].[left+1,right]…[right-1,right]均满足条件,因此,计数加
right-left+1
,然后移动右边界(滑动区间加大),看剩下的区间是否满足乘积小于k,如果小于k,重复步骤1,否则进行步骤2。
步骤2:当滑动窗口内的乘积大于等于k时,右移左边界(滑动区间减小),如果这个区间内乘积小于k,进入步骤1,否则重复步骤2
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
# 初始化
left = 0
product = 1
res = 0
# 右指针先移动
for right in range(len(nums)):
product *= nums[right]
# 乘积大于等于k时,移动左指针
while left <= right and product >= k:
product /= nums[left]
left += 1
# 循环结束条件为:product < k
# 统计当前右边界下的子数组个数
res += (right - left + 1)
return res
在一排树中,第 i 棵树产生 tree[i] 型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:
- 把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
- 移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。
你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。
用这个程序你能收集的水果树的最大总量是多少?
问题可以抽象为:要求一个最长的连续子序列,同时这个子序列的元素种类不能超过2种
用一个右指针right和左指针left。右指针用于往右探,左指针用于指示每一个新的子序列要从前面那个序列里继承哪一位。那么继承哪一位呢?实际上继承的是翻转的那一位。例如[1,2,1,3,3,3],right往右遍历的时候,如果当前的种类与left的种类不同,那么就令left=right,其中i就指示了下一个子序列的起点。
class Solution:
def totalFruit(self, tree: List[int]) -> int:
ans = 0 # 总量
n = len(tree)
left, right = 0, 0
types = {
} # 篮子
while right < n:
# 当前水果不可加入
if len(types) == 2 and types.get(tree[right]) is None:
types = {
}
right = left # j回退到最后一次翻转的位置
else: # 当前水果可加入
types[tree[right]] = types.get(tree[right], 0) + 1
if tree[right] != tree[left]: # 水果有翻转 用i记录下翻转的位置
left = right
right += 1
ans = max(ans, sum(types.values()))
return ans
思路:
模拟一个滑动窗口,维护变量 i 是最小的下标满足 [i, j] 是合法的子序列。
维护 count 是序列中各种类型的个数,这使得我们可以很快知道子序列中是否含有 3 种类型。
class Solution(object):
def totalFruit(self, tree):
ans = i = 0
# 字典的子类,提供了可哈希对象的计数功能
count = collections.Counter()
for j, x in enumerate(tree):
count[x] += 1
while len(count) > 2:
# i指针右移
count[tree[i]] -= 1
if count[tree[i]] == 0:
del count[tree[i]]
i += 1
ans = max(ans, j - i + 1)
return ans