滑动窗口通过双指针维护无重复字符子串,右指针扩展、左指针精准跳转至重复字符上次位置+1,哈希表记录索引确保o(n)时间复杂度,left需取max避免回退。

滑动窗口是解决“无重复字符的最长子串”这类子串问题最直观、高效的方法,核心在于用双指针维护一个窗口,保证窗口内字符不重复,并动态伸缩窗口来更新最大长度。
窗口怎么定义和移动?
用左右两个指针 left 和 right 表示当前窗口的边界(闭区间),初始都指向 0。右指针不断向右扩展,把字符加入窗口;一旦遇到重复字符,就移动左指针直到窗口重新满足“无重复”条件。
- 右指针每次前进一格,把
s.charAt(right)加入记录(比如用HashMap<character integer></character>存每个字符最后出现的位置) - 若当前字符已存在且其位置 ≥ left,说明它在当前窗口里重复了,就把 left 移到该字符上次位置的下一位(
left = map.get(ch) + 1) - 每次更新后,用
right - left + 1计算当前窗口长度,并更新最大值
为什么用哈希表而不是 Set?
只用 Set 可以判断是否重复,但无法知道重复字符上一次出现在哪——这会导致 left 指针盲目右移,可能跳过更优解或破坏窗口连续性。用 Map 记录字符最新索引,能让 left 精准跳到重复位置之后,避免回退或遗漏。
- 例如字符串
"tmmzuxt",当 right 到第二个'm'(索引 2)时,map 中'm'→1,left 可直接设为 2,跳过无效试探 - 如果用 set + while 循环逐个删左端字符,时间复杂度会退化为 O(n²),而 map 方案保持 O(n)
边界处理要注意什么?
left 指针不能往回走,即使新重复字符的位置比当前 left 还小,也要取 max(left, 上次位置+1)。
- 比如
"abba":当 right=2(第二个'b'),map 中'b'→1,left 更新为 2;但 right=3(第二个'a')时,map 中'a'→0,此时 0+1=1 - 代码中写成
left = Math.max(left, map.get(ch) + 1)就能安全处理
完整逻辑怎么组织?
遍历 right 从 0 到 len-1,每步做三件事:查重、调 left、更新答案。不需要额外清空 map,因为只关心“当前窗口内”的重复,历史旧索引被新索引自然覆盖即可。
- 初始化:
int left = 0, maxLen = 0;Map<character integer> map = new HashMap()</character> - 循环体:
char ch = s.charAt(right)→ 若 map 含 ch,更新 left →map.put(ch, right)→maxLen = Math.max(maxLen, right - left + 1) - 返回
maxLen即可
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











