LeetCode 第3题 无重复字符的最长子串(滑动窗口(双指针)+ HashSet)
一、核心算法思想
采用滑动窗口(双指针)+ HashSet,时间复杂度 \(O(n)\)。
- 定义窗口区间 \([i,rk]\):代表当前正在考察、无重复字符的连续子串;
- i:窗口左边界(外层循环遍历);rk:窗口右边界,只会向右移动,不回退;
- HashSet 集合
occ:实时保存当前窗口内部所有字符,快速判断字符是否重复; - 流程:
- 左边界i右移时,将移出窗口的字符从集合删除;
- 在不重复前提下,尽可能向右扩张右边界 rk;
- 每次扩张停止后,计算窗口长度,更新全局最长长度答案
ans。
区分概念:子串必须连续;子序列可以不连续。
二、完整带注释代码
class Solution { public int lengthOfLongestSubstring(String s) { // occ = occurrence,存储当前滑动窗口内的字符 Set<Character> occ = new HashSet<>(); int n = s.length(); int rk = -1, ans = 0; // rk右指针初始-1;ans=answer保存最终结果 for(int i = 0; i < n; i++){ if(i != 0){ // 左边界右移,把离开窗口的字符移除集合 occ.remove(s.charAt(i-1)); } // 右边界持续扩张:下一个字符不越界、且窗口不存在该字符 while(rk + 1 < n && !occ.contains(s.charAt(rk + 1))){ occ.add(s.charAt(rk + 1)); rk++; } // 更新最长子串长度 ans = Math.max(ans, rk - i + 1); } return ans; } }三、实例运行推演
输入:s = "abcabcbb"
| i (左边界) | 操作 | rk | 窗口\([i,rk]\) | occ 集合 | 当前窗口长度 | ans 最大值 |
|---|---|---|---|---|---|---|
| i=0 | i=0 无需 remove;持续右扩 rk | 2 | [0,2] | {a,b,c} | 3 | ans=3 |
| i=1 | 删除 s [0] 字符 a;右扩 rk | 3 | [1,3] | {b,c,a} | 3 | ans=3 |
| i=2 | 删除 s [1] 字符 b;右扩 rk | 4 | [2,4] | {c,a,b} | 3 | ans=3 |
| i=3 | 删除 s [2] 字符 c;右扩 rk | 5 | [3,5] | {a,b,c} | 3 | ans=3 |
| i=4 | 删除 s [3] 字符 a;无法继续右扩 | 5 | [4,5] | {b,c} | 2 | ans=3 |
| i=5 | 删除 s [4] 字符 b;右扩 rk | 6 | [5,6] | {c,b} | 2 | ans=3 |
| i=6 | 删除 s [5] 字符 c;无法继续右扩 | 6 | [6,6] | {b} | 1 | ans=3 |
最终返回 ans = 3
四、关键语法与内置方法、基础类型 & 包装类积累
1. String 内置方法
s.length():获取字符串长度,必须带括号;数组长度写法arr.length(无括号)s.charAt(index):String 内置方法,根据下标获取对应字符;⚠️方法名全小写charAt,禁止写成CharAt
2. Java 基础类型与对应包装类
核心规则:Java 中
Set、List、Map等集合泛型不支持基础数据类型,必须使用包装类。支持自动装箱、自动拆箱,无需手动转换。
| 基础类型(基本类型) | 对应包装类 | 刷题场景示例 |
|---|---|---|
| byte | Byte | Set<Byte> |
| short | Short | Map<Short,String> |
| int | Integer | Set<Integer>(两数之和、最长连续序列高频) |
| long | Long | Map<Long,Integer> |
| float | Float | 极少用到 |
| double | Double | 极少用到 |
| boolean | Boolean | Set<Boolean> |
| char | Character | 本题Set<Character> |
3. HashSet 常用方法
occ.add():字符加入集合occ.remove():删除指定字符occ.contains():判断字符是否存在集合内
4. 通用工具方法
Math.max(a,b):返回两个数字中较大的值,用于更新最长长度
五、高频易错点汇总
- 大小写错误:
s.CharAt()❌ 正确:s.charAt() - 缺少调用对象:不能单独写
charAt(),必须写成s.charAt(下标) - 混淆
length与length():字符串不要漏写括号 - 逻辑误区:不要一次性把全部字符放入集合;集合
occ只保存当前窗口内字符- 窗口扩张 → add;窗口左移收缩 → remove;保持集合与窗口内容同步
- rk 初始值设为
-1:窗口初始为空,保证第一轮循环可以访问下标 0 的字符 if(i != 0)判断:i=0 是第一轮,窗口左侧没有移出元素,无需执行 remove
六、变量名称释义(刷题通用简写)
- occ:occurrence,窗口内已经出现的字符集合
- rk:right index,滑动窗口右指针
- i:滑动窗口左指针
- ans:answer,保存最终答案(最长无重复子串长度)
