【数据结构与算法第四期:滑动窗口算法经典题型解析:思路、解法与对比】

🚀 作者 :“码上有前”
🚀 文章简介 :数据结构与算法
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬
请添加图片描述

在这里插入图片描述

滑动窗口算法经典题型解析:思路、解法与对比

摘要

本文聚焦 LeetCode 中 4 道滑动窗口经典题目(长度最小的子数组、无重复字符的最长子串、串联所有单词的子串、最小覆盖子串 ),深入剖析滑动窗口解题思路,提供 Python 解法,提炼技巧要点。同时补充其他可行方法,从时间复杂度、空间复杂度、适用场景多维度对比,助力读者透彻理解滑动窗口算法的应用逻辑与优势边界 。

关键词

滑动窗口;LeetCode 算法;时间复杂度;空间复杂度;算法对比

一、引言

滑动窗口算法作为双指针算法的重要分支,在处理数组、字符串的子串(子数组)相关问题时表现卓越,能高效将嵌套循环的高复杂度问题优化为线性复杂度。本文选取 4 道典型题目,详细拆解滑动窗口解法,对比其他方法,明晰算法选型逻辑。

二、题目解析与多解法对比

(一)209. 长度最小的子数组

1. 滑动窗口法(核心解法)
思路

用滑动窗口(双指针 leftright )维护子数组,right 扩展窗口,当窗口内元素和 ≥ 目标值时,尝试移动 left 缩小窗口,更新最小长度。

代码(Python)
def minSubArrayLen(target, nums):
    left = 0
    sum_val = 0
    min_len = float('inf')
    for right in range(len(nums)):
        sum_val += nums[right]
        while sum_val >= target:
            min_len = min(min_len, right - left + 1)
            sum_val -= nums[left]
            left += 1
    return min_len if min_len != float('inf') else 0
技巧

通过窗口的动态伸缩(right 扩展、left 收缩 ),线性遍历找到满足条件的最小子数组,时间复杂度 O(n)O(n)O(n)

复杂度
  • 时间:O(n)O(n)O(n)leftright 各遍历数组一次 。
  • 空间:O(1)O(1)O(1) ,仅用常数变量 。
2. 前缀和 + 二分查找法(其他方法)
思路

计算前缀和数组,利用前缀和的单调性,对每个 right ,二分查找满足 prefix[right] - prefix[left] ≥ target 的最小 left ,更新最小长度。

代码(Python)
import bisect

def minSubArrayLen(target, nums):
    prefix = [0]
    for num in nums:
        prefix.append(prefix[-1] + num)
    min_len = float('inf')
    for right in range(1, len(prefix)):
        target_val = prefix[right] - target
        left = bisect.bisect_left(prefix, target_val, 0, right)
        if prefix[right] - prefix[left] >= target:
            min_len = min(min_len, right - left)
    return min_len if min_len != float('inf') else 0
复杂度
  • 时间:O(nlog⁡n)O(n \log n)O(nlogn) ,前缀和构建 O(n)O(n)O(n) ,二分查找 O(log⁡n)O(\log n)O(logn) 每次,共 nnn 次 。
  • 空间:O(n)O(n)O(n) ,存储前缀和数组 。
对比
维度滑动窗口法前缀和 + 二分法
时间复杂度O(n)O(n)O(n)O(nlog⁡n)O(n \log n)O(nlogn)
空间复杂度O(1)O(1)O(1)O(n)O(n)O(n)
适用场景追求线性时间,空间敏感数组非负(保证前缀和单调)场景

滑动窗口法更高效,无需额外空间存储前缀和;前缀和 + 二分法依赖数组非负性(保证前缀和单调 ),空间开销大。

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

1. 滑动窗口 + 哈希表法(核心解法)
思路

用滑动窗口(leftright )维护子串,哈希表记录字符最后出现位置。right 扩展时,若字符重复,更新 leftmax(left, 重复字符上一位置 + 1) ,同步更新最长子串长度。

代码(Python)
def lengthOfLongestSubstring(s):
    char_map = {}
    left = 0
    max_len = 0
    for right in range(len(s)):
        if s[right] in char_map:
            left = max(left, char_map[s[right]] + 1)
        char_map[s[right]] = right
        max_len = max(max_len, right - left + 1)
    return max_len
技巧

利用哈希表快速判断字符是否重复及定位上一位置,保证窗口内无重复字符,线性遍历求解,时间复杂度 O(n)O(n)O(n)

复杂度
  • 时间:O(n)O(n)O(n)leftright 遍历字符串一次 。
  • 空间:O(k)O(k)O(k)kkk 为字符集大小(如英文字母则 k=26k = 26k=26 ) 。
2. 暴力枚举法(其他方法)
思路

枚举所有子串,检查是否无重复字符,记录最长长度。

代码(Python)
def lengthOfLongestSubstring(s):
    max_len = 0
    n = len(s)
    for i in range(n):
        for j in range(i + 1, n + 1):
            if len(set(s[i:j])) == j - i:
                max_len = max(max_len, j - i)
    return max_len
复杂度
  • 时间:O(n3)O(n^3)O(n3) ,枚举子串 O(n2)O(n^2)O(n2) ,检查重复 O(n)O(n)O(n)
  • 空间:O(k)O(k)O(k) ,存储集合判断重复 。
对比
维度滑动窗口 + 哈希表法暴力枚举法
时间复杂度O(n)O(n)O(n)O(n3)O(n^3)O(n3)
空间复杂度O(k)O(k)O(k)O(k)O(k)O(k)
适用场景常规解题,追求高效理解子串枚举逻辑场景

滑动窗口法高效解决问题,暴力法仅适用于极小数据量,无实际应用价值。

(三)30. 串联所有单词的子串

1. 滑动窗口 + 哈希表法(核心解法)
思路

单词长度固定(设为 word_len ),遍历起始位置 0 ~ word_len - 1 。对每个起始位置,用滑动窗口截取长度为 word_len 的片段,哈希表记录单词出现次数,匹配目标单词组合,更新结果。

代码(Python)
from collections import Counter

def findSubstring(s, words):
    if not s or not words:
        return []
    word_len = len(words[0])
    word_count = Counter(words)
    total_len = word_len * len(words)
    res = []
    for start in range(word_len):
        left = start
        current_count = Counter()
        for right in range(start, len(s), word_len):
            word = s[right:right + word_len]
            if word not in word_count:
                left = right + word_len
                current_count.clear()
                continue
            current_count[word] += 1
            while current_count[word] > word_count[word]:
                left_word = s[left:left + word_len]
                current_count[left_word] -= 1
                left += word_len
            if right - left + word_len == total_len:
                res.append(left)
    return res
技巧

利用单词长度固定的特性,分起始位置遍历;哈希表精准匹配单词出现次数,窗口滑动步长为 word_len ,保证效率。

复杂度
  • 时间:O(wordlen×n)O(word_len \times n)O(wordlen×n)nnn 为字符串长度,word_len 次遍历,每次 O(n/wordlen)O(n / word_len)O(n/wordlen)
  • 空间:O(m)O(m)O(m)mmm 为单词数量(存储哈希表 ) 。
2. 暴力枚举 + 哈希表法(其他方法)
思路

枚举所有可能的子串起始位置,截取对应长度子串拆分单词,用哈希表对比是否匹配目标单词组合。

代码(Python)
from collections import Counter

def findSubstring(s, words):
    if not s or not words:
        return []
    word_len = len(words[0])
    total_len = word_len * len(words)
    word_count = Counter(words)
    res = []
    for i in range(len(s) - total_len + 1):
        sub_s = s[i:i + total_len]
        sub_words = [sub_s[j:j + word_len] for j in range(0, total_len, word_len)]
        if Counter(sub_words) == word_count:
            res.append(i)
    return res
复杂度
  • 时间:O((n−m×l)×m)O((n - m \times l) \times m)O((nm×l)×m)nnn 为字符串长度,mmm 为单词数量,lll 为单词长度,截取子串、拆分、对比均有开销。
  • 空间:O(m)O(m)O(m) ,存储哈希表 。
对比
维度滑动窗口 + 哈希表法暴力枚举 + 哈希表法
时间复杂度O(wordlen×n)O(word_len \times n)O(wordlen×n)O((n−m×l)×m)O((n - m \times l) \times m)O((nm×l)×m)
空间复杂度O(m)O(m)O(m)O(m)O(m)O(m)
适用场景单词数量多、字符串长场景单词数量少、字符串短场景

滑动窗口法通过固定步长和窗口伸缩,大幅优化时间;暴力法在数据量大时极慢,仅适合小数据验证逻辑。

(四)76. 最小覆盖子串

1. 滑动窗口 + 哈希表法(核心解法)
思路

用滑动窗口(leftright )维护子串,哈希表记录目标字符需求。right 扩展包含目标字符,left 收缩优化窗口,当窗口满足包含所有目标字符时,更新最小覆盖子串。

代码(Python)
from collections import Counter

def minWindow(s, t):
    need = Counter(t)
    window = Counter()
    left = 0
    valid = 0
    start, min_len = 0, float('inf')
    for right in range(len(s)):
        c = s[right]
        if c in need:
            window[c] += 1
            if window[c] == need[c]:
                valid += 1
        while valid == len(need):
            if right - left + 1 < min_len:
                start = left
                min_len = right - left + 1
            d = s[left]
            if d in need:
                if window[d] == need[d]:
                    valid -= 1
                window[d] -= 1
            left += 1
    return "" if min_len == float('inf') else s[start:start + min_len]
技巧

借助哈希表精准控制窗口内目标字符的覆盖情况,通过 valid 变量判断是否满足条件,动态更新最小子串,时间复杂度 O(n)O(n)O(n)

复杂度
  • 时间:O(n)O(n)O(n)leftright 遍历字符串一次 。
  • 空间:O(k)O(k)O(k)kkk 为目标字符集大小 。
2. 暴力枚举法(其他方法)
思路

枚举所有子串,检查是否包含目标所有字符,记录最小长度子串。

代码(Python)
from collections import Counter

def minWindow(s, t):
    min_len = float('inf')
    result = ""
    t_count = Counter(t)
    for i in range(len(s)):
        for j in range(i + 1, len(s) + 1):
            sub_s = s[i:j]
            sub_count = Counter(sub_s)
            if all(sub_count[c] >= t_count[c] for c in t_count):
                if len(sub_s) < min_len:
                    min_len = len(sub_s)
                    result = sub_s
    return result
复杂度
  • 时间:O(n3)O(n^3)O(n3) ,枚举子串 O(n2)O(n^2)O(n2) ,检查字符 O(n)O(n)O(n)
  • 空间:O(k)O(k)O(k) ,存储哈希表 。
对比
维度滑动窗口 + 哈希表法暴力枚举法
时间复杂度O(n)O(n)O(n)O(n3)O(n^3)O(n3)
空间复杂度O(k)O(k)O(k)O(k)O(k)O(k)
适用场景实际解题、数据量大场景理解子串覆盖逻辑场景

滑动窗口法高效解决问题,暴力法仅用于理论理解,无法处理中等及以上数据量。

三、总结

滑动窗口算法通过动态调整窗口边界(双指针伸缩 ),将子串(子数组 )问题的高复杂度优化为线性或近似线性。对比其他方法(前缀和、暴力枚举等 ),在适配场景下(如处理需动态维护区间的问题 ),展现出时间与空间的双重优势。不同题目需结合数据特点(有序性、字符重复性等 )与复杂度要求选型,掌握滑动窗口算法的核心逻辑与对比要点,能显著提升算法解题的效率与精准度,从容应对数组、字符串的区间类问题挑战 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码上有前

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

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

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

打赏作者

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

抵扣说明:

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

余额充值