Skip to content

03-滑动窗口

📅 发表于 2026/04/02
🔄 更新于 2026/04/02
👁️ -- 次访问
📝 0 字
0 分钟

总结

一些概念

小心

异位词

  • abcbca为异位词
  • 直接判断counter是否相等即可。

覆盖子串

  • abcd为覆盖abc的串。
  • 需要判断字符数量 大于等于

题目总结

1-003-无重复字符的最长子串

题目

  • 求解字符串s中,连续最长不重复子串长度

暴力法

  • 依次遍历每个字符作为起始向后求解,计算当前子串长度。

滑动窗口左右指针法

  • 左右指针定义窗口子串[l,r]遍历r,求解每个窗口长度r-l+1
  • 为保证窗口内无重复字符
    • 若s[r]出现过左侧指针需要往前移动,到s[r]出现位置的下一个位置
    • l只能往前需要取max,避免连续重复字符导致回退。
  • 记录每个字符出现位置
2-438-找到字符串中所有字母异位词

题目

  • 长字符串s短字符串p,求解s中p的所有字母异位子串固定窗口大小

暴力法

  • 遍历s每个字符,作为窗口起始值 左指针,判断窗口子串p是否异位词。 (Key)

左右指针统计次数法

  • 异位词:使用Counter统计p窗口子串字符次数
  • 遍历右指针
    • 右指针进入计算窗口大小旧左指针出去
    • 计算窗口值
3-560-和为K的子数组

题目

  • 给一个nums数组和一个目标求和值k,计算和为k连续子数组数量

前缀和哈希解法

  • 记录前缀和 出现次数
  • 遍历数组,计算当前前缀和减去K,计算需要前缀和
  • 从Hash中,提取需要前缀和的数量
4-239-滑动窗口最大值

题目

  • 给定nums数组,和滑动窗口大小k。依次输出每个窗口内的最大值

双端单调队列滑动窗口队头最大值法

  • 滑动窗口遍历right边界计算left边界
  • 双端队列,保证单调递增队首>队尾队首作为窗口最大值存储下标
  • 队首过期出队:超出left边界。
  • 新队尾入队:保证为队列最小值;若旧队尾小于当前值,则需循环出队
5-076-最小覆盖子串

题目

  • 给字符串s和t,计算s中覆盖t中所有字符最短子串

子串覆盖

  • 某字符覆盖窗口字符次数 >= t中字符次数

滑动窗口右扩展左收缩找最短覆盖

  • 遍历右指针右指针进入窗口
  • 当前窗口满足
    • 更新窗口大小
    • 左指针循环收缩找最小窗口。窗口不满足则停止收缩
    • 右指针扩张
  • 当前窗口不满足右指针扩张

题目

1-003-无重复字符的最长子串

1-003-无重复字符的最长子串

leetcode-003

  • 输入:字符串s,可能有多个相同不同的字符
  • 输出:字符串里最长不重复子串长度
  • 示例:abcdbcd -> abcd -> 4aaaa -> a -> 1

暴力法

暴力法
  • 遍历每个字符,依次计算其作为起始字符的最大长度
  • 设置字符集合依次往后找,如果某个字符出现过,则停止。
python
def lengthOfLongestSubstring_force_loop(self, s: str) -> int:
    """ 暴力遍历,从每个字符开始,往后查找
    """
    if not s:
        return 0
    n = len(s)
    max_length = 1
    for i in range(n):
        cur_sets = set()
        cur_sets.add(s[i])  
        j = i + 1
        while j < n and s[j] not in cur_sets:  
            cur_sets.add(s[j])
            j += 1
        max_length = max(max_length, len(cur_sets))
    return max_length

滑动窗口左右指针法

滑动窗口左右指针法

核心思想

  • 设立左右指针固定 左指针遍历 右指针
  • 每次计算当前窗口 [l, r]长度r-l+1
  • 需保证窗口内部 无重复字符
    • 如果当前r位置字符 s[r] 已出现过,则需要移动左指针l
    • 左指针l移动到s[r]重复位置下一个位置
    • l新位置 需取max不能往回走
  • 更新当前字符所在位置

左指针不能往回走

  • 比如abba例子,l=2, r=3, 但若直接下一个位置,则l=1往回走导致重复
python
def lengthOfLongestSubstring_leftright_sliding_window(self, s: str) -> int:
    if not s:
        return 0
    n = len(s)

    # 左右双指针
    l = r = 0
    # 记录字符出现的位置
    ch2pos = {}
    max_len = 0

    # 右指针往前走,左指针看情况往前走,每次循环计算一个窗口 [l,r]
    while r < n:  
        # 当前窗口最右侧的字符
        ch = s[r]
        if s[r] in ch2pos:  
            # 需要保证 [l, r] 之间无重复字符
            # 当前字符已出现过,l指针需往前移动到出现字符的下一个位置,但是不能往回走。
            # 比如abba这个例子,l=2, r=3, 但若是直接下一个位置,则l=1,往回走,导致重复
            l = max(ch2pos[ch] + 1, l)
        # 更新位置
        ch2pos[ch] = r
        # 当前窗口长度
        cur_len = r - l + 1
        max_len = max(max_len, cur_len)
        # 右指针继续往前走
        r += 1
    return max_len

2-438-找到字符串中所有字母异位词

2-438-找到字符串中所有字母异位词

leetcode-438

  • 输入:长字符串s,短字符串p。
  • 输出:s中 所有p的异位词子串位置

相关题目

暴力穷举判断异位词

暴力穷举判断异位词
  • 依次遍历s每个字符,作为窗口起始值。判断当前窗口p是否是异位词
  • 异位词判断排序后 生成字符串key判断 key相同即可。
  • 会超时
python
def findAnagrams_force_loop(self, s: str, p: str) -> List[int]:
    res = []
    if not s or not p:
        return []

    # p的key和窗口大小
    pkey = "".join(sorted(p))
    window_size = len(p)
    for i in range(len(s)):
        j = i + window_size  
        if j > len(s):
            break
        # 当前窗口值
        subs = s[i:j]
        subs_key = "".join(sorted(subs))
        if pkey == subs_key:  
            res.append(i)
    return res

滑动窗口左右指针统计次数法

滑动窗口左右指针统计次数法

窗口异位词判断

  • 使用Counter()类,直接判断 cnt_p == cnt_s,即字符出现次数 是否相等

核心思想

  • 统计p字符串滑动窗口字符出现次数
  • 遍历右指针 做循环
    • 右指针字符进入,新元素+1
    • left<0 :窗口大小不足继续循环
    • left>0窗口溢出左指针字符 出去,旧元素-1
    • 计算当前窗口内容cnt_p == cnt_s

滑动窗口三字经

  • s_count[新元素] += 1
  • if 长度超标: s_count[旧元素] -= 1
  • if s_count == p_count: 记录答案
python
def findAnagrams_leftright_window(self, s: str, p: str) -> List[int]:
    res = []
    if not s or not p or len(s) < len(p):
        return res
    # 直接统计p字符串以及窗口中的字符出现次数
    cnt_p = Counter(p)   
    cnt_s = Counter()  

    for right, ch in enumerate(s):
        # 右侧字符+1
        cnt_s[ch] += 1
        # 窗口左侧位置
        left = right - len(p) + 1 
        if left < 0:
            # 窗口大小不足
            continue
        if left > 0:
            # 窗口起始位置>0,上一轮的左侧位置减1
            pre_left = left - 1
            cnt_s[s[pre_left]] -= 1
        # 直接判断两个counter是否相似,自动忽视值为0的项
        if cnt_p == cnt_s:  
            res.append(left)
    return res

3-560-和为K的子数组

3-560-和为K的子数组

leetcode-560

  • 输入:数组nums整数和k
  • 输出:和为k连续子数组 数量

前缀和加补数求数量

前缀和加补数求数量

两个定义

  • 前缀和 pre[i]:数组元素位置0到i求和

    s[i]=pre[i]=j=0inums[j]
  • 哈希计数:统计前缀和具体值出现次数

核心思想

  • 定义累计前缀和 current_sum、前缀次数 count、初始化count[0]=1
  • 遍历每个元素
    • 计算含当前位置元素前缀和pre[i]
    • 计算当前位置补数补数 = 前缀和 - 求和k
    • 如果补数存在,则次数 += 补数次数

注意

  • 并非单调递增,不能用 滑动窗口。

示例

  • [1, 2, 3, 4, 5]K=7,正确应该是 [3, 4] 子数组
  • 前缀和:[1, 3, 6, 10, 15]
  • 补数前缀和:[-6, -3, -1, 3, 8]
  • 补数前缀和3 出现1次,则次数+1。把[1,2]去掉剩下[3,4],则和为K=7
python
def subarraySum(self, nums: List[int], k: int) -> int:
    """
    前缀和+Hash记录次数
    """
    if not nums:
        return 0

    # 累计前缀求和
    current_sum = 0
    # 前缀出现次数
    count = defaultdict(int)
    # 必须初始化前缀和0为1
    # 因为 1, 2, 3。若是k=3,到1 2的时候,need_sum=0,正好1,2算一个
    count[0] = 1

    res = 0
    for num in nums:
        # 当前前缀和
        current_sum += num
        # 当前位置所需的前缀
        need_sum = current_sum - k
        if need_sum in count:
            # 累加之前的所有统计值
            res += count[need_sum]  
        # 更新当前前缀和
        count[current_sum] += 1
    return res

4-239-滑动窗口最大值

4-239-滑动窗口最大值

leetcode-239

  • 输入:数组nums,固定滑动窗口 大小k。从左到右移动,每次只能看k个数字
  • 输出:所有滑动窗口内的最大值组成的列表

双端单调队列窗口队首最值法

双端单调队列窗口队首最值法

双端队列

  • 单调递增,队首 > 队尾,每次窗口取队首作为最大值队尾最小值
  • 存储下标,方便判断过期。

滑动窗口核心思想

  • 遍历右指针 做循环
    • 计算left左侧边界
    • 队首出队:队首过期,不在窗口内小于left索引
    • 队尾出队保证 队尾为最小值需小于nums[r]循环出队
    • 当前元素nums[r] 入队
    • 当前left-right 满足窗口大小,则从队首当前窗口最大值
python
def maxSlidingWindow_queue(self, nums: List[int], k: int) -> List[int]:
    """ 使用单调队列:保持先进先出单调递增,当前值超过队首最大值,则队列清空后再入队;当前值小于队尾值,则直接入队。队首超期,也出队
    """
    res = []
    if not nums or len(nums) < k or k <= 0:
        return res 

    # 队列
    queue = deque(maxlen=k)  

    # 循环遍历每个元素,作为右侧窗口值
    n = len(nums)

    for right in range(n):
        # 滑动窗口左侧值
        left = right - k + 1

        # 1. 队首过期出队,不在滑动窗口里
        if len(queue) > 0 and queue[0] < left:  
            queue.popleft()

        # 2. 队尾检查,保证队尾元素大于当前值
        while len(queue) > 0 and nums[queue[-1]] < nums[right]:  
            # 队尾出队
            queue.pop()

        # 3. 当前元素入队
        queue.append(right)

        # 4. 计算滑动窗口值
        if left >= 0:
            res.append(nums[queue[0]])

    return res

5-076-最小覆盖子串

5-076-最小覆盖子串

leetcode-076

  • 输入:字符串st
  • 输出:s中覆盖 t中所有字符最短子串
  • 示例:s=BECODEBANC,t=ABC,最小子串=BANC

滑动窗口右扩展左收缩

滑动窗口右扩展左收缩方法

子串覆盖判断

  • 字符hash,各字符分别判断。
  • 需要:窗口字符次数 >= 目标字符次数大于等于很重要,并不只是等于。

滑动窗口右扩展左收缩

  • 右指针先一直往右走,直到当前窗口包含最短子串
  • 左指针一直往右走,直到当前窗口不包含最短子串

核心链路

  • 循环遍历右指针,右指针进入窗口
  • 当前窗口不满足
    • 右指针+1,右指针扩展
  • 当前窗口满足
    • 计算更新窗口大小
    • 循环 左指针收缩寻找最小窗口
      • 当前窗口满足:更新窗口大小;左指针继续收缩
      • 当前窗口不满足:break掉
    • 右指针+1,右指针扩展
python
def minWindow_lrwindow(self, s: str, t: str) -> str:
    """滑动窗口,右指针扩充,左指针收缩
    """
    if not s or not t or len(s) < len(t):
        return ""

    # 字符串计数
    cnt_w = defaultdict(int)
    cnt_t = defaultdict(int)

    for ch in t:
        cnt_t[ch] += 1

    # 左右指针
    l = r = 0

    def match_substr():
        match_all = True
        for ch, cnt in cnt_t.items():
            # 窗口内的cnt需要大于等于目标cnt
            if cnt_w[ch] < cnt:  
                match_all = False
                break
        return match_all

    res = ""
    while r < len(s):
        # 右指针进入窗口
        cnt_w[s[r]] += 1

        # 判断当前窗口是否满足
        match_all = match_substr()

        if match_all:
            # 当前窗口满足子串,计算更新窗口
            if not res or r-l+1 < len(res):
                res = s[l:r+1]

            # 左指针向右收缩,寻找最小子串
            while l+1 <= r:  
                cnt_w[s[l]] -= 1
                l += 1
                match_all = match_substr()
                if match_all:
                    if r-l+1 < len(res):
                        res = s[l:r+1]
                else: 
                    break
            # 右指针继续往前走
            r += 1
        else:
            # 当前窗口不满足,右指针扩展
            r += 1

    return res
总访客数:   ·   总访问量:
PLM's Blog @ 2016 - 2026