#3
中等
高频
滑动窗口无重复字符的最长子串
这是一道围绕哈希表、字符串、滑动窗口展开的高频练习。建议先掌握「滑动窗口」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
哈希表
字符串
滑动窗口
双指针
题目分析
给你一个字符串,你要在里面找到一段连续子串,这段子串里的字符不能重复,并返回这段子串的最大长度。
比如 abcabcbb,最开始的 abc 就满足条件,长度是 3。继续往后扩展时会遇到重复字符,所以答案最终还是 3。
一句话概括:
在字符串里,找到一段没有重复字符的最长连续区间。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:用滑动窗口维护一段始终没有重复字符的区间,右边扩张,冲突时左边直接跳。
推荐代码
推荐解法:滑动窗口
时间复杂度: O(n)
空间复杂度: O(min(m,n))
核心思路: 用滑动窗口维护一段始终没有重复字符的区间,右边扩张,冲突时左边直接跳。
class Solution {
/**
* 滑动窗口解法
* 使用左右指针维护一个不包含重复字符的窗口
*/
public int lengthOfLongestSubstring(String s) {
// 使用HashMap记录字符最后出现的位置
Map<Character, Integer> charMap = new HashMap<>();
int left = 0;
int maxLength = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
// 如果字符已存在且在当前窗口内,移动左指针
if (charMap.containsKey(c) && charMap.get(c) >= left) {
left = charMap.get(c) + 1;
}
// 更新字符位置
charMap.put(c, right);
// 更新最大长度
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
}结构化讲解
面试时怎么讲
开场思路
这题是典型的滑动窗口。我要维护一段始终不含重复字符的合法区间。
核心过程
- 右指针负责不断把新字符纳入窗口。
- 我用哈希表记录每个字符最近一次出现的位置。
- 如果当前字符在窗口里重复出现,就把左指针直接跳到重复字符上一次位置的后一位。
- 每轮窗口合法后,用当前长度更新最大答案。
复杂度总结
时间复杂度 O(n),空间复杂度 O(min(m,n))。
面试补一句:这题真正值得记住的是“左指针直接跳”,而不是机械背窗口模板。
核心思路
这题的关键不是“会不会双指针”,而是能不能把窗口不合法时的处理写成一次跳转。哈希表记录字符最近出现位置,负责把左边界快速推进到合法位置。
步骤讲解
1
初始化窗口和哈希表
用 left 表示窗口左边界,用哈希表记录每个字符最近一次出现的位置。
为什么这样做:窗口负责维护区间,哈希表负责在冲突时快速定位应该跳到哪里。
对应代码提示:Map<Character, Integer> lastIndex = new HashMap<>();
2
右指针不断扩张窗口
让 right 从左到右扫描字符串,把新字符加入当前考虑范围。
为什么这样做:题目要求的是最长连续子串,所以所有候选区间都来自这个单向扩张过程。
对应代码提示:for (int right = 0; right < s.length(); right++) { ... }
3
遇到重复字符时移动左边界
如果当前字符在窗口内已经出现过,就把 left 直接跳到它上一次出现位置的后一位。
为什么这样做:重复字符左边那一段已经不可能继续留在合法窗口里,直接跳比一点点缩更高效。
对应代码提示:if (lastIndex.containsKey(current) && lastIndex.get(current) >= left) { left = lastIndex.get(current) + 1; }
4
更新字符最新位置
不管是否发生重复,都把当前字符的最新下标写回哈希表。
为什么这样做:下一次再遇到这个字符时,只有最近一次出现位置才有意义。
对应代码提示:lastIndex.put(current, right);
5
实时更新窗口最大长度
每轮都用 right - left + 1 更新答案。
为什么这样做:窗口始终保持合法,所以当前窗口长度天然就是一个可用候选答案。
对应代码提示:answer = Math.max(answer, right - left + 1);
易错点
左指针不能回退
如果当前字符之前出现过,但位置已经在窗口左边界左侧,就不应该把 left 拉回去。
正确理解:只有当上一次位置
>= left 时,才更新左边界。忘记更新字符最近位置
如果哈希表里保留的是旧位置,下一次遇到重复字符时,左边界跳转就会出错。
正确理解:每轮处理完当前字符后,都要执行
lastIndex.put(current, right)。把题目理解成子序列
这题要求的是连续子串,不允许跳过中间字符。
正确理解:整个写法必须基于连续窗口
[left, right],不能做不连续选择。复杂度与适用判断
时间复杂度:O(n)
空间复杂度:O(min(m,n))
比其他方案更好在哪里:比枚举所有子串再检查重复的暴力法快得多,把重复判断压成了哈希表查询。
适用判断:只要题目是“最长/最短连续区间 + 某个约束条件”,都应该优先联想到滑动窗口。
额外提醒
- 窗口是否始终合法,是滑动窗口题的核心不变量。
- 哈希表记录最近位置后,左边界可以直接跳,不需要逐个删除字符。
动画演示
如果你还没完全看懂,可以展开动画演示。
滑动窗口动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
滑动窗口动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
准备就绪 - 点击开始按钮查看动画
动画演示仅在桌面端提供,移动端请优先阅读推荐代码与结构化讲解。