2024.3.(16,17号)力扣刷题记录-滑动窗口学习记录

文章介绍了如何使用滑动窗口和双指针技巧解决一系列编程问题,包括查找长度最小的子数组、乘积小于K的子数组、无重复字符的最长子串等,同时讨论了代码优化,如使用哈希表存储和减少计算次数,以及如何处理特定题目的解法
摘要由CSDN通过智能技术生成

一、学习视频:滑动窗口【基础算法精讲 03】_哔哩哔哩_bilibili

二、视频跟练代码

1.题目:209. 长度最小的子数组

方法:双指针

代码:

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # if sum(nums) < target:
        #     return 0
        l = 0
        n = len(nums)
        cnt = inf
        for r in range(n):
            while sum(nums[l+1:r+1]) >= target:     
                #注意这里是l+1不然退出循环,l和r之间和会小于target
                l += 1
            if sum(nums[l:r+1]) >= target:     #才开始时,r-l+1较小,会出错
                cnt = min(cnt, r-l+1)
        return cnt if cnt <= n else 0

我根据视频的思路写的代码,居然超时了。仔细观察发现,我使用切片求和的话,每一次判断都要求一次,这样时间就长了。于是更改为了和视频一样的用参数保存,修改代码如下:

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if sum(nums) < target:
            return 0
        l = 0
        n = len(nums)
        cnt = inf
        sum_ = 0
        for r in range(n):
            sum_ += nums[r]
            while sum_ - nums[l] >= target:     
                #注意这里-num[l],不然退出循环,l和r之间和会小于target
                sum_ -= nums[l]
                l += 1
            if sum_ >= target:     #才开始时,r-l+1较小,会出错
                cnt = min(cnt, r-l+1)

 或

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if sum(nums) < target:
            return 0
        l = 0
        n = len(nums)
        cnt = inf
        sum_ = 0
        for r in range(n):
            sum_ += nums[r]
            # # 代码1
            # while sum_ - nums[l] >= target:     
            #     #注意这里-num[l],不然退出循环,l和r之间和会小于target
            #     sum_ -= nums[l]
            #     l += 1
            # if sum_ >= target:     #才开始时,r-l+1较小,会出错
            #     cnt = min(cnt, r-l+1)
            # #
            # 代码2
            while sum_ >= target:       #操作循环一起
                cnt = min(cnt, r-l+1)   #先更新后变化   #但是这样比代码1慢,因为每一次while循环都要更新一次,而代码1不是
                sum_ -= nums[l]
                l += 1
            #
        return cnt # if cnt <= n else 0

注意:这里的时间复杂度是O(n)的,而不是O(n^2)。因为循环次数和L,R移动的次数有关,但是L,R之间是没有关系的;L最多只移动n次,R也是。是相加的关系而不是相乘。

2024.3.17续:

2.题目:713. 乘积小于 K 的子数组

方法:双指针

代码:

class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        if k <= 1:      #注意k == 1时,也无法满足
            return 0
        l = 0
        ans = 0
        mutiple = 1
        for r in range(len(nums)):
            mutiple *= nums[r]
            while mutiple >= k:
                mutiple /= nums[l]
                l += 1
            # for _ in range(l,r+1):      #以r为右端点的数组的所有子数组
            #     ans += 1
            ans += r-l+1
        return ans

3.题目:3. 无重复字符的最长子串

方法:滑动窗口双指针

代码:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 滑动窗口双指针
        n = len(s)
        if n <= 1:
            return n
        ans = -inf
        # 字符串储存
        chars = ''
        l = 0
        for r,x in enumerate(s):
            while x in chars:
                l += 1
                chars = chars[1:]   #去掉第一个
            chars += x    #字符串拼接
            ans = max(ans,r-l+1)
        return ans

还可以使用哈希表储存,时间比用字符串储存快。代码参考视频,代码如下:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 滑动窗口双指针
        n = len(s)
        if n <= 1:
            return n
        ans = -inf
        # 哈希表储存
        cnt = Counter()    # hashmap  key-char,value-count  #这里类似初始化,使用Counter函数
        l = 0
        for r,c in enumerate(s):
            cnt[c] += 1
            while cnt[c] > 1:
                cnt[s[l]] -= 1
                l += 1
            ans = max(ans,r-l+1)
        return ans

时间复杂度O(n)。空间复杂度(重点!!!): O(128)(ASCII码个数);O(1);O(len(set(s))去重后s的个数(根据视频)。

三、课后作业练习

1.题目:2958. 最多 K 个重复元素的最长子数组

代码:

class Solution:
    def maxSubarrayLength(self, nums: List[int], k: int) -> int:
        # 滑动窗口
        if k <= 0:
            return 0
        ans = 0
        hash = {}   #hash表
        l = 0
        for r,x in enumerate(nums):
            hash[x] = hash.get(x,0) + 1
            while hash[x] > k:      #只要每一个x频率小于等于k,就能满足都小于等于k
                hash[nums[l]] -= 1
                l += 1
            ans = max(ans,r-l+1)
        return ans

2.题目: 2730. 找到最长的半重复子字符串

代码:

class Solution:
    def longestSemiRepetitiveSubstring(self, s: str) -> int:
        # 滑动窗口
        # if len(s) == 1:     #下标从1开始,要先验证下标为一
        #     return 1
        # ans = 0
        ans = 1     #包含长为1时
        flag = 0    #标记相等相邻字符的个数
        l = 0       #l从0开始,l和r之间的是所需字符串
        for r in range(1,len(s)):
            if s[r] == s[r-1]:
                flag += 1
            while flag > 1:
                if s[l] == s[l+1]:
                    flag -= 1
                l += 1
            ans = max(ans,r-l+1)
        return ans

3.题目:1004. 最大连续1的个数 III

代码:

class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        # 滑动窗口
        n = len(nums)
        l = 0
        ans = 0
        cnt = 0
        for r,x in enumerate(nums):
            # k -= 1-x    #为了不改变k
            cnt += 1-x
            # while k < 0:
            while cnt > k:
                # if nums[l] == 0:
                #     cnt -= 1
                cnt -= 1-nums[l]
                l += 1
            ans = max(ans,r-l+1)
        return ans

 4.题目:2962. 统计最大元素出现至少 K 次的子数组

代码:

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        if k > len(nums):
            return 0
        cnt = 0
        ans = 0
        maxnum = max(nums)
        l = 0
        for r,x in enumerate(nums):
            if x == maxnum:
                cnt += 1
            while cnt >= k:
                if nums[l] == maxnum:
                    cnt -= 1
                l += 1
            ans += l    #注意!!右端点不同,数组不同
        return ans

5.题目:2302. 统计得分小于 K 的子数组数目

代码:

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        # 数组之和大于1递增,数组长度大于1递增,整体是递增的
        # 所以是先满足小于k,后大于等于k
        # 所以设置while循环条件为大于等于k
        ans,l,sum_ = 0,0,0
        for r,x in enumerate(nums):
            sum_ += x
            while sum_ * (r-l+1) >= k:
                sum_ -= nums[l]
                l += 1
            ans += r-l+1
        return ans

 6.题目:1658. 将 x 减到 0 的最小操作数

代码:

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        #反过来想,就是连续数组和为sum(nums)-x
        #最小操作数就是子数组长度最长
        # if min(nums) > x:
        #     return -1
        goal = sum(nums) - x
        if goal < 0:    #注意!
            return -1
        l,ans,sum_ = 0,-1,0
        for r,num in enumerate(nums):
            sum_ += num
            while sum_ > goal:
                sum_ -= nums[l]
                l += 1
            if sum_ == goal:
                ans = max(ans,r-l+1)
        return -1 if ans < 0 else len(nums)-ans     #数组长度减去子数组长度

感谢你看到这里!一起加油吧!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值