leetcode解题-滑动窗口

本文深入探讨了滑动窗口算法的应用,通过两个LeetCode题目,详细解析了如何使用滑动窗口解决最长无重复子串及最小子数组求和的问题,展示了算法在不同场景下的灵活运用。

滑动窗口思路:
解决部分数组问题时,设置两个索引下标i,j{i,j}i,ji{i}i为左边界,j{j}j为右边界,逐渐遍历整个数组,i{i}ij{j}j组成的子数组形成长度变化的滑动窗口,直至i{i}i遍历完整个数组。

应用一:

Leetcode 3:Longest Substring Without Repeating Characters
在一个字符串中寻找没有重复字母的最长子串,返回长度值。
解法1:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 滑动窗口
        result = {}
        left = 0
        ans = 0
        
        for i, c in enumerate(s):
            # 把所有的字符存放在字典里,每次对一个字符进行检查:
            # if c在result里,且result[c]的索引>= left【说明字符重复了】
            #     更新left为索引值+1
            # else 最大子串长度=i-left+1或者当前的最大值
            # 更新/添加新字符的索引。【始终保持当前value是某字符索引的最大值】
            
            if c in result and result[c]>=left:
                left = result[c]+1
            else:
                ans = max(i-left+1, ans)
            result[c]=i
        return ans

解法2:

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 滑动窗口,右边界右移,直到找到有重复字符,记录长度,然后将左边界右移到重复的数组+1位置,再继续右移右边界,直到找到新的重复字符
        # 时间复杂度O(n),空间复杂度O(1)
        freq = [0 for _ in range(256)]  # 用于记录每个字符串当前出现频率,索引为每个字符串对应的ASCII码
        l = 0
        r = -1  # nums[l...r]为滑动窗口
        res = 0  # 初始化为最小
        while l < len(s):
            # 当右边界下一个位置s[r+1]的频率为0时才右移,否则有重复字符,就右移左边界直到没有重复字符
            if r < len(s)-1 == 0 and freq[ord(s[r+1])] == 0:
                r += 1
                freq[ord(s[r])] += 1
            else:
                freq[ord(s[l])] -= 1
                l += 1
            # 每次循环中滑动窗口内永远不会有重复字符,所以每次都可以做比较,最终res为最大值
            res = max(res, r-l+1)
        return res

应用二:

Leetcode 209:Minimum Size Subarray Sum
给定一个整型数组和一个数字s,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum>=s,返回这个最短的连续子数组的长度值。

class Solution(object):
    def minSubArrayLen(self, nums):
        """
        :type s: int
        :type nums: List[int]
        :rtype: int
        """
        # 滑动窗口,右边界右移,找到一个连续子数组和大于s,记录长度,然后将左边界右移,当和小于s时,再右移右边界,直到找到新的连续子数组
        # 时间复杂度O(n),空间复杂度O(1)
        l = 0
        r = -1  # nums[l...r]为滑动窗口
        sums = 0
        res = len(nums) + 1 # 初始设为不可能取到的最大值
        while l < len(nums):
            if sums < s and r < len(nums) - 1:
                r += 1
                sums += nums[r]
            else:
                sums -= nums[l]
                l += 1
            if sums >= s:
                res = min(res, r-l+1)
        if res == len(nums) + 1:
            return 0
        return res
### 解题思路 LeetCode 第 1456 题“定长子串中元音的最大数目”要求找出长度为 `k` 的连续子串中,元音字符数量最多的那个子串。这个问题可以通过**固定大小滑动窗口(Fixed-size Sliding Window)**的方法高效解决。 算法的核心思想是维护一个大小为 `k` 的窗口,并在每次移动窗口时更新当前窗口内的元音总数。初始阶段计算前 `k` 个字符的元音数量,之后每次向右滑动一位时,移除窗口左端的字符(如果它是元音),并添加新进入窗口的右端字符(如果它是元音)。每一步都记录最大值,最终得到结果。 该方法的时间复杂度为 O(n),其中 n 是字符串的长度,因为每个字符仅被访问两次(一次加入窗口,一次移出窗口)[^1]。 ### C++ 实现代码 ```cpp #include <unordered_set> #include <string> using namespace std; class Solution { public: int maxVowels(string s, int k) { unordered_set<char> vowels = {'a', 'e', 'i', 'o', 'u'}; int cnt = 0; // 初始化窗口 for (int i = 0; i < k; ++i) { if (vowels.count(s[i])) { ++cnt; } } int ans = cnt; // 滑动窗口遍历字符串 for (int i = k; i < s.size(); ++i) { if (vowels.count(s[i - k])) { --cnt; // 移除窗口最左边的字符 } if (vowels.count(s[i])) { ++cnt; // 添加新进入窗口的字符 } ans = max(ans, cnt); } return ans; } }; ``` ### 关键点分析 - **元音判断**:使用 `unordered_set` 存储元音字符,以便快速判断某个字符是否是元音。 - **滑动窗口初始化**:从字符串开头构建第一个窗口,并统计其中的元音数量。 - **窗口滑动过程**:每次滑动时更新计数器,避免重复遍历整个窗口。 - **性能优化**:相比暴力解法(O(n * k)),滑动窗口将时间复杂度优化至 O(n),适用于大规模输入数据。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值