news 2026/3/28 20:02:57

【小白笔记】无重复字符的最长子串和长度最小的子数组(滑动窗口中两种不同的“窗口控制策略)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【小白笔记】无重复字符的最长子串和长度最小的子数组(滑动窗口中两种不同的“窗口控制策略)


这个问题是“滑动窗口 (Sliding Window)”算法的顶级经典题。

在处理“最长子串”、“子数组”等问题时,滑动窗口能够将复杂度从O(N2)O(N^2)O(N2)降低到O(N)O(N)O(N)


1. 核心思路:滑动窗口

想象字符串上有一个可以伸缩的窗口

  1. 右边界 (right)不断向右移动,把字符纳入窗口。
  2. 检查重复:如果新加入的字符已经在窗口里了,说明窗口不合法了。
  3. 左边界 (left)被迫向右收缩,直到把那个重复的字符“踢出去”为止。
  4. 记录最大值:在窗口合法的每一步,记录窗口的长度(right - left + 1),取最大值。

2. 优化工具:哈希表 (Map/Dict)

为了快速知道某个字符是否在窗口内,以及它在什么位置,我们使用一个哈希表来记录:

  • Key: 字符本身。
  • Value: 该字符最近一次出现的索引位置

3. 代码实现 (Python)

classSolution:deflengthOfLongestSubstring(self,s:str)->int:# 哈希表记录字符最后出现的位置char_map={}left=0max_len=0forrightinrange(len(s)):char=s[right]# 如果当前字符已经在哈希表中,且它的索引在窗口内ifcharinchar_mapandchar_map[char]>=left:# 关键:左边界直接跳到重复字符上次出现的下一个位置left=char_map[char]+1# 更新/记录当前字符的最新位置char_map[char]=right# 计算当前窗口长度并更新最大值max_len=max(max_len,right-left+1)returnmax_len

4. 为什么left能够“直接跳跃”?

假设字符串是a b c d b e f,当right走到第二个b时:

  • 窗口内是a b c d
  • 新来的b和索引为1b重复了。
  • 我们不需要把left一步步挪。我们知道,只要left还在索引1及其左边,窗口就永远包含两个b
  • 所以,left直接跳到1 + 1 = 2的位置(即字符c),窗口瞬间变合法。

5. 复杂度分析

  • 时间复杂度:O(N)O(N)O(N)。虽然有for循环,但leftright都只是一路向右,没有回退。
  • 空间复杂度:O(min(M,N))O(min(M, N))O(min(M,N))NNN是字符串长度,MMM是字符集的大小(比如 ASCII 码只有 128 个)。

总结:滑动窗口的“模版”

这种题通常遵循一个套路:

  1. :右指针进位,加入新元素。
  2. :判断当前窗口是否满足条件(本题是判断重复)。
  3. :如果不满足,左指针出位,直到满足为止。
  4. :在窗口合法时更新结果。


这个问题紧接着“最长子串”的逻辑,但目标相反:这次我们要找的是满足条件的“最短”连续区间

这依然是滑动窗口的经典应用,但窗口的收缩时机发生了变化。


1. 核心思路:动态窗口(“进”与“缩”)

想象你手里拿着一个弹簧:

  1. 右边界right负责“伸”:不断把数字加入窗口,直到窗口内的总和sum >= target
  2. 左边界left负责“缩”:一旦总和达标,说明我们找到了一个可行解。但为了找“最小”长度,我们要尝试从左侧缩减窗口,看看在总和依然达标的前提下,能不能让窗口更短。
  3. 更新结果:在每次缩小窗口的过程中,记录最小的窗口长度。

2. 代码实现 (Python)

classSolution:defminSubArrayLen(self,target:int,nums:List[int])->int:n=len(nums)left=0current_sum=0# 初始化为一个不可能达到的大值min_len=float('inf')forrightinrange(n):# 1. 右指针进位,增加总和current_sum+=nums[right]# 2. 只要当前和满足条件,就开始尝试收缩左边界whilecurrent_sum>=target:# 更新最小长度 (当前窗口长度为 right - left + 1)min_len=min(min_len,right-left+1)# 尝试从左边踢出一个数,看看剩下的还够不够current_sum-=nums[left]left+=1# 3. 如果 min_len 没变过,说明没找到满足条件的子数组return0ifmin_len==float('inf')elsemin_len

3. 为什么这个逻辑能行?(关键点)

  • 为什么用while而不是if
    因为在剔除左边的一个数后,剩下的和可能依然大于等于target。我们需要一直缩,直到和变得小于target为止,这样才能确保我们不漏掉任何一个可能的最小长度。
  • 为什么不需要回头看?
    因为数组里的数都是正整数。右移left必然会让和变小,右移right必然会让和变大。这种单调性保证了滑动窗口的有效性。

4. 复杂度分析

  • 时间复杂度O(N)O(N)O(N)
    虽然代码里有for循环嵌套while循环,但仔细观察:每个元素最多被right访问一次,最多被left访问一次。整个过程就像两只虫子在爬,每个指针都只走了一遍数组。
  • 空间复杂度O(1)O(1)O(1)。只用了几个变量。

总结:滑动窗口的“伸缩”哲学

  • 求最长:右指针主动移,左指针被动跟(发现不合规才动)。
  • 求最短:右指针主动移,左指针主动追(只要合规就拼命缩,直到不合规)。

进阶思考

这道题能用滑动窗口的前提是:数组中全是正整数
如果数组中包含负数,窗口的和就不再具有“右移必大,左移必小”的单调性了。那种情况下,我们需要用到另一种技术——前缀和 + 哈希表
这是一个非常敏锐的观察!你指出的其实是滑动窗口中两种不同的“窗口控制策略”。

虽然两道题整体都是O(N)O(N)O(N),但因为问题的**性质(求最长 vs 求最短)**不同,导致左指针(left)的移动逻辑完全不同。


1. 无重复最长子串(求最长):if逻辑(或单次跳跃)

在求最长时,我们是在**“发现不合规时,被迫移动一次左指针”**。

  • 逻辑:窗口一直向右扩展,直到新来的字符造成了重复。
  • 动作:此时我们只需要把left移动到重复字符上次出现的下一个位置。这个动作是一次性的。
  • 代码体现
    ifcharinchar_mapandchar_map[char]>=left:left=char_map[char]+1# 瞬间跳过去,不需要循环
    即便不使用哈希表记录位置,而是用while剔除,它也只是为了恢复合规。一旦合规,我们就停止移动left,继续增加right来追求“更长”。

2. 长度最小子数组(求最短):while逻辑

在求最短时,我们是在**“只要合规,就拼命压榨左指针”**。

  • 逻辑:当窗口和满足sum >= target时,这只是一个“可行解”。为了找到“最优解(最短)”,我们要看看缩掉左边一个数后,是不是依然合规。
  • 动作
    1. 合规了?记录长度,删掉左边的,再看看还合规吗?
    2. 还合规?再记录长度,再删掉左边的…
      这个过程必须用while,因为你可能可以连续删掉好几个数字,窗口依然满足条件
  • 代码体现
    whilecurrent_sum>=target:min_len=min(min_len,right-left+1)# 记录current_sum-=nums[left]# 尝试缩小left+=1# 循环继续

3. 核心差异对比

题目类型目标left指针的任务循环结构原因
求最长(3题)尽可能推迟left移动恢复合规。只要没重复,left就不动。冲突通常是单个点,处理完该点即可。
求最短(209题)尽可能压迫left移动探索极限。只要合规,就拼命缩。合规是一个区间,必须通过循环找到区间的最小临界点。

总结

  • 最长子串:右指针是“先锋”,左指针是“底线”。只有底线被触碰了(重复),左指针才动。
  • 最短数组:右指针是“补给”,左指针是“追求”。补给一够(达标),左指针就立刻出发去寻找更短的可能。

如果你把“最短数组”里的while改成if,你就会漏掉很多情况。比如target=7, nums=[1,1,1,1,7],当right走到7时,和变成了11。用if你只能缩短一次变成[1,1,1,7],而用while它可以一路缩到只剩[7],这才是真正的最短。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/21 10:00:41

测试基础|执行验收测试需要注意哪些?

通过本文的介绍,供大家了解验收测试的重要性以及它如何帮助开发测试人员确保软件应用程序满足所需的规范。 概述 验收测试涉及从用户的角度验证应用程序的验收,评估软件是否符合业务要求,以确定其是否可以发布。 在软件开发生命周期(Softw…

作者头像 李华
网站建设 2026/3/25 15:37:53

算法题 二进制表示中质数个计算置位

二进制表示中质数个计算置位 问题描述 给你两个整数 left 和 right,请你找到在 [left, right] 范围内,计算置位位数为质数的整数个数。 计算置位:指二进制表示中 1 的个数。质数:大于 1 且只能被 1 和自身整除的数。 注意&…

作者头像 李华
网站建设 2026/3/26 12:31:14

β-Amyloid (1-42), Rat;DAEFGHDSGFEVRHQKLVFFAEDVGSNKGAIIGLMVGGVVIA

一、基础性质英文名称:β-Amyloid (1-42), Rat;Amyloid β-Protein (1-42), Rat;Rat Aβ1-42中文名称:大鼠源 β- 淀粉样蛋白 (1-42);大鼠 β- 淀粉样肽 (1-42)单字母多肽序列:DAEFGHDSGFEVRHQKLVFFAEDVGSN…

作者头像 李华
网站建设 2026/3/27 1:05:16

β-Amyloid (25-35);GSNKGAIIGLM

一、基础性质英文名称:β-Amyloid (25-35);Amyloid β-Protein (25-35);Aβ25-35中文名称:β- 淀粉样蛋白 (25-35);β- 淀粉样肽 (25-35)单字母序列:GSNKGAIIGLM(标准 Aβ25-35 序列&#xff09…

作者头像 李华
网站建设 2026/3/26 23:48:41

冰 挂——冬日的风景线

冬日群峰,俨然一座童话秘境。溪流收住奔涌的脚步,倒挂悬崖,凝作晶莹剔透的冰景。 有的如乳白软酪,柔润饱满,铺满整面山壁;有的似银白玉柱,清透晶亮,挺立于崖下石阶;有的像…

作者头像 李华