中等LeetCode 3可逐步演示引导学习
无重复字符的最长子串
滑动窗口:右扩左收
用左右指针维护一个"没有重复字符"的窗口:right 负责扩张,遇到重复字符就把 left 往右收,全程记录窗口的最大长度。
时间复杂度 O(n)空间复杂度 O(k)(k 为字符集大小)
所属套路:滑动窗口框架
题单:面试热题
题目描述
给定一个字符串 s,请找出其中不含重复字符的最长子串,返回它的长度。(子串是连续的字符片段,不是子序列。)
学习目标
能判断什么题适合滑动窗口,说清"右扩左收"为什么是 O(n),并写出窗口长度与收缩时机的正确实现。
已完成 0 / 6 阶段
诊断
先判断:这题为什么能用窗口
什么样的题适合滑动窗口?"连续子串"和"子序列"的区别在这里为什么重要?
建议先完成当前阶段(提示 / 检查 / 自评),再进入下一步;也可以直接跳过。
解题思路
核心思想
用左右指针维护一个"没有重复字符"的窗口:right 负责扩张,遇到重复字符就把 left 往右收,全程记录窗口的最大长度。
为什么需要它
暴力做法要枚举所有子串再逐个检查有没有重复,代价高到不可接受。而子串问题有个天然性质:如果 [left, right] 里出现了重复,那么任何包含它的更长区间一定也重复——所以窗口只需要"右扩左收",永远不用回头重新检查。
第一反应:直觉方案
两层循环枚举所有起点和终点,对每个子串用一个集合检查是否含重复字符。能出答案,但 n 稍大就爆炸。
关键洞察
关键洞察是"窗口内的信息可以增量维护":right 每走一步只引入一个新字符,所以只需要判断这个新字符在不在窗口里;一旦重复,就收缩 left 直到重复消失——而不是重新检查整个窗口。每个字符最多被加入一次、移出一次,总操作 O(n)。这就是滑动窗口的本质:把"枚举所有区间"压缩成"每个元素进出窗口各一次"。
时间复杂度 O(n)空间复杂度 O(k)(k 为字符集大小)
易错点
- 收缩要用 while 而不是 if——可能要连续移出多个字符,直到重复消失
- 窗口长度是 right - left + 1,不是 right - left
- 移出的是最左边的 s[left],别写成刚扫到的 s[right]
逐步演示
参考代码
1function lengthOfLongestSubstring(s) {
2 let left = 0;
3 let best = 0;
4 const window = new Set();
5 for (let right = 0; right < s.length; right++) {
6 while (window.has(s[right])) {
7 window.delete(s[left]);
8 left += 1;
9 }
10 window.add(s[right]);
11 best = Math.max(best, right - left + 1);
12 }
13 return best;
14}
效果图
预测:答对 0 / 共 1 题
第 1 / 15 步