滑动窗口问题总结

在一开始接触滑动窗口时,可能会觉得无从下手,但是相关题目做多了以后,就可以整理出一套框架,搞清楚套路后滑动窗口问题也并不是特别的难。
需要注意的是,滑动窗口包括定长和不定长窗口,在一些细节上还是有区别的。

一、643. 子数组最大平均数 I

在这里插入图片描述

题解

这个题目本质上是一个滑动定窗口问题,定义两个指针left,right 分别是窗口的左右边界。当right-left+1==k时,窗口形成,若大于k则左边界向前走。只有窗口形成时才进行求平均值的操作。

代码

class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -> float:
            if not nums or not k:
                return 0

            sum = 0
            max_average = -float('inf')
            left,right = 0,0
            # 创建窗口
            while right < len(nums):
                sum += nums[right]
                # 判断窗口是否创建完毕,如果窗口已形成,就计算平均值
                if right-left+1 == k:
                    average = sum/k
                    max_average = max(max_average,average)
                # 当超过窗口的范围时,left指针就缩小范围
                if right-left+1 >= k:
                    sum -= nums[left]
                    left += 1
                right += 1

            return max_average

二、3. 无重复字符的最长子串

在这里插入图片描述

题解

滑动窗口问题可以当做队列来看,队列的特性就是先进先出
当窗口已满或者不符合要求时,就应该出队,也就是操作原序列的左边。

代码

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
            if not s:
                return 0
            max_count = 0

            que = []
            # 遍历整个序列
            for right in range(len(s)):
                # 如果已在队列里,就进行出队操作
                while s[right] in que and que:
                    que.pop(0)
                # 否则进队
                que.append(s[right])
                # 返回最大的长度
                max_count = max(max_count,len(que))

            return max_count

三、209. 长度最小的子数组

在这里插入图片描述

题解

题目中说的很明确,连续子数组,这不就是一个滑动不定窗口吗?对于滑动不定窗口,先移动right,直到不符合条件,再移动left

代码

def minSubArrayLen(nums,target):
    if not target and not nums:
        return 0
    sum = 0
    res = float('inf')
    left,right = 0,0 # 窗口的左右边界
    while right < len(nums):
        sum += nums[right]
        while sum >= target:
            subLength = right-left+1 # 窗口长度
            res = min(subLength,res)
            sum -= nums[left]
            left += 1
        right += 1

    return res

四、1456. 定长子串中元音的最大数目

在这里插入图片描述

题解

滑动定窗口,还要新建一个判断是否是元音字母的函数

代码

'''
滑动窗口问题,算是比较简单的题目
'''
class Solution:
    def maxVowels(self, s: str, k: int) -> int:
        def isYuan(s):
            if s=='a' or s=='e' or s=='i'or s=='o'or s=='u':
                return 1
            else:
                return 0
        
        count = 0
        max_num = 0

        if not s or not k:
            return 0

        right = 0
        while right<len(s):
            # 依次向后遍历,判断是否是元音字母
            count += isYuan(s[right])
            right += 1
            # 窗口已经形成,如果要再判断,必须从窗口前面去除
            if right>=k:
                # 对符合要求的最大长度进行更新
                max_num = max(max_num,count)
                # 窗口大小是固定的,必须先取出一个,才可以进行插入
                count -= isYuan(s[right-k])

        return max_num

代码二,感觉这个比较好理解,更形象一点

'''
滑动窗口问题,算是比较简单的题目
'''
class Solution:
    def maxVowels(self, s: str, k: int) -> int:
        def isYuan(s):
            if s=='a' or s=='e' or s=='i'or s=='o'or s=='u':
                return 1
            else:
                return 0
        
        count = 0 # 窗口中元音字母的个数
        max_count = 0 # 最多的元音字母的个数

        left,right = 0,0
        while right <len(s):

            count += isYuan(s[right])

            if right-left+1 == k:
                max_count = max(max_count,count)
            right += 1
            if right-left+1 > k:
                count -= isYuan(s[left])
                left += 1


        return max_count

五、1695. 删除子数组的最大得分

在这里插入图片描述
看来我真得好好学学语文,这个题目愣是没看懂是啥意思,看了评论区才明白。
给你一个正整数数组 nums ,求累加和最大的无重复元素的连续子数组,返回其累加和的值。

题解

使用队列来解决这个问题,只要遍历到一个新的元素,这个元素一定会入队,并且如果队中已有这个元素,则一直出队,直到队中无重复元素。

代码

'''给你一个正整数数组 nums ,求累加和最大的无重复元素的连续子数组,返回其累加和的值。'''
class Solution:
    def maximumUniqueSubarray(self, nums: List[int]) -> int:
        res = 0
        que = collections.deque()
        maxN = 0
        for i in range(len(nums)):
            maxN += nums[i]
            # 如果队列中有这个数,则将之前面的全部出队
            while nums[i] in que:
                temp = que.popleft()
                maxN -= temp
            
            que.append(nums[i])
            res = max(maxN, res)
        return res

六、438. 找到字符串中所有字母异位词

在这里插入图片描述

题解

这个题目我个人认为是在滑动窗口问题中比较难的,这是一个滑动定窗口,窗口的大小就是p的长度。
因为题目中已经说了,全都是小写字母,所以可以新建一个长度为26的数组,这个数组用来存储哪个字母出现过以及出现了几次。当然,数组中的值,是元素相对于字母a的相对位置。这个相对位置可以通过ord()来实现。
如果不理解while循环,一定要自己debug一下或者手写实现。

代码

def findAnagrams_re(s,p):
    m,n = len(s),len(p)
    if n>m:
        return None
    s_cnt = [0]*26
    p_cnt = [0]*26
    res = []
    # 对p_cnt进行初始化
    for i in range(n):
        p_cnt[ord(p[i])-ord('a')] += 1
    left = 0
    # 遍历s数组
    for right in range(m):
        cur_right = ord(s[right])-ord('a')
        s_cnt[cur_right] += 1
        # 如果这个字母在p中未出现过,或者在s中出现的次数大于在p中出现的次数,移动左窗口
        while s_cnt[cur_right]>p_cnt[cur_right]:
            cur_left = ord(s[left])-ord('a')
            s_cnt[cur_left] -= 1
            left += 1
        # 如果窗口形成,则把左边界加入到res中
        if right-left+1 == n:
            res.append(left)
    return res

七、567. 字符串的排列

这个题目和上面那个题目简直是一模一样。
在这里插入图片描述

题解

就不多说啦,如果有符合条件的窗口,就直接返回True就好了。

代码

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
            if not s1 or not s2:
                return False
            m,n = len(s1),len(s2)
            if n<m:
                return False
            s1_cnt = [0]*26
            s2_cnt = [0]*26

            for i in range(m):
                s1_cnt[ord(s1[i])-ord('a')] += 1

            left = 0
            for right in range(n):
                cur_right = ord(s2[right])-ord('a')
                s2_cnt[cur_right] += 1

                while s2_cnt[cur_right]>s1_cnt[cur_right]:
                    cur_left = ord(s2[left])-ord('a')
                    s2_cnt[cur_left] -= 1
                    left += 1

                if right-left+1 == m:
                    return True

            return False

八、1004. 最大连续1的个数 III

在这里插入图片描述
这个题目真的很好,个人觉得很有意思。其实就是一开始想不出思路,看了别人的思路觉得恍然大悟。

题解

还是滑动窗口的老方法,既然是窗口了,你的左右边界得有吧,即left,right。其他参数用到什么定义什么。定义zeros记录窗口中0的个数,当zeros>k时,窗口左边界移动。

代码

class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
            if not nums:
                return None

            left = 0
            max_count = 0
            zeros = 0
            # 遍历数组
            for right in range(len(nums)):
                if nums[right]==0:
                    zeros += 1
                # 移动左边界
                while zeros>k:
                    if nums[left]==0:
                        zeros -= 1
                    left += 1
                # 返回结果
                max_count = max(max_count,right-left+1)
                
            return max_count

九、1052. 爱生气的书店老板

在这里插入图片描述
题目描述是有问题的,customers[i] 是在第 i 分钟开始时进入商店的顾客的数量,并非编号。可以查看英文描述。
在这里插入图片描述

题解

第一种解法
1.我们可以先将原本就满意的客户加入答案,同时将对应的 customers[i]置为0。
2.之后的问题转化为:在 customers中找到连续一段长度为 minutes的子数组,使得其总和最大。
当时看到这个解法,真的太牛了,看来自己在算法路上还是道阻且长

第二种解法
这个解法就是滑动窗口比较通俗的解法,估计看了上面的题目,这个应该也会看得明白。就是多加了if语句判断。

代码

class Solution:
    def maxSatisfied(self, customers: List[int], grumpy: List[int], minutes: int) -> int:
        sum_ = 0
        for i in range(len(customers)):
            if grumpy[i] == 0:
                sum_ += customers[i]
                customers[i] = 0 # 一定记得置为0

        max_,cur = 0 ,0
        for i in range(len(customers)):
            cur += customers[i]
            if i >= minutes:
                cur -= customers[i-minutes]

            max_ = max(cur,max_)

        return sum_ + max_

class Solution:
    def maxSatisfied(self, customers: List[int], grumpy: List[int], minutes: int) -> int:
         # 所有不生气的时间内的顾客总数
        sum_ = 0
        for i in range(len(customers)):
            if grumpy[i] == 0:
                sum_ += customers[i]
        # 生气的时间区间内,会让多少顾客不满意
        cur_value = 0
        # 先计算起始区间
        for i in range(minutes):
            if grumpy[i] == 1:
                cur_value += customers[i]

        # 不满意顾客的最大值
        res_value = cur_value
        for i in range(minutes,len(customers)):
            if grumpy[i]==1:
                cur_value += customers[i]
            if grumpy[i-minutes]==1:
                cur_value -= customers[i-minutes]

            res_value = max(res_value,cur_value)

        return sum_ + res_value

十、1423. 可获得的最大点数

在这里插入图片描述
有一说一,这个题目的思路我也觉得巨好

题解

题目中让我们求可获得的最大点数,可能一开始很难和滑动窗口问题关联起来,从两边进行操作这怎么用滑动窗口啊。其实这个题目变一下,你肯定立马可以想到解法,让你求一固定长度的窗口内的数值和最小的区间。最后让总和减去这个最小的就可以得到我们想要的答案了。

代码

'''
找到长度为window_size的最小区间,再减去就好了
'''
class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        n = len(cardPoints)
        window_size = n-k # 窗口大小
        left,right,sum_ = 0,0,0
        min_value = float('inf') # 最小的窗口和
        window_value = 0 # 窗口值

        while right<n:
            sum_ += cardPoints[right] # 数组中全部的和
            window_value += cardPoints[right]
            # 如果超过窗口长度,左边界后移
            if right-left+1 > window_size:
                window_value -= cardPoints[left]
                left += 1
            # 进行一系列的操作
            if right-left+1 == window_size:
                min_value = min(min_value,window_value)
            right += 1

        return sum_ - min_value

这个题目其实可以当做滑动定窗口题目的模板

# 先遍历数组
# 	进行一系列操作(求和等)
# 	当超过窗口长度时
# 		进行一系列操作
# 		左边界右移
#     当等于窗口长度时
#         结合题意求出答案
#     返回结果

总结

滑动窗口的题目大概做了四五天,掌握了顾虑还是比较好解决的,我一直在想,不能让自己走的那么“快”,即囫囵吞枣的刷题,要让自己慢下来,稳一点。这样也许效果会更好吧。
这篇文章从上午九点就开始写,一直写到下午四点,也算又回顾了一下。
道阻且长

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ap21ril

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值