LeetCode 热题 100刷题记录-8.无重复字符的最长子串
想要在互联网进出离职求职游刃有余,笔试算法题真的必不可少,年轻人听句劝!关注这个系列,每天更新。
记录解题过程,便于个人复盘和追踪进度。分为基础信息、题目分析、解题代码、复盘总结四部分。
题目基础信息
题目名称:无重复字符的最长子串
题目链接:无重复字符的最长子串
难度等级:中等
标签分类:数组
完成日期:25.8.20
题目:给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
**人话版:**一个字符串, 找出里面长度最长的不重复子串,这个 子串的长度 。
题目分析
核心问题:
(用1-2句话概括题目本质)
找出长度最长的不重复子串的长度是多少。
关键约束:
无
初始思路:
我有想法,我想到了要创建一个集合,如果里面不存在,就放进去。我也想到了如果当前这个元素和前一个元素重复了,就说明得把里面的删除了,重新开始往里加。但是我就是做不出来。因为我一直在死磕,具体的最长子序列是什么,而不是有多长。然后返回这个集合的长度就可以了,结果理想很美好,现实很残酷。(代码如下第一版 — 全是问题啊bro)
优化方向:
- 妙极了,当我把思维聚集在长度,而不是整个子串是什么的时候就开始好起来了
- 具体是碰到重复的就将头部一个删除,一直删到不重复为止,每次把最大值更新。
原始数组:abcabcbb , -> 集合变化:abc -> bca -> cab -> abc -> bc -> c -> cb -> b- 虽然说整个集合最后只剩一个
b在里头, 但是最长子串的长度max,早已被我记录。 - 上面这个就是算法的核心。具体见详细的注释和代码。
输入: “abcabcbb”
初始:[a],max = 1
加入 b → [a,b],max = 2
加入 c → [a,b,c],max = 3
下一个 a 已存在 → 移除左边 a → [b,c]
再次检查 a → 加入 → [b,c,a],max = 3
继续往后走……最后结果 3
解题代码
# 示例:最长子序列 拉跨版 只能通过小部分测例
public int lengthOfLongestSubstring(String s) {
// 将字符串转为字符数组,方便逐个处理
char[] chars = s.toCharArray();
// 特殊情况:如果字符串长度为 0,直接返回 0
if (0 == chars.length) {
return 0;
}
// 用 HashSet 来存储当前子串的字符,保证无重复
HashSet<Character> temp = new HashSet<>();
// 先把第一个字符放入集合
temp.add(chars[0]);
// 从第二个字符开始遍历
for (int i = 1; i < chars.length; i++) {
// 如果当前字符和前一个字符相同,
// 并且集合大小 < 剩余未遍历的字符数,
// 就清空集合(重新开始记录)
// ❌ 这一段逻辑其实有问题:
// 无重复子串的判断不是只看相邻字符是否相等。
if (chars[i] == chars[i - 1] && temp.size() < chars.length - i) {
temp.clear();
}
// 如果当前字符没有出现在集合中,就加入集合
if (!temp.contains(chars[i])) {
temp.add(chars[i]);
}
}
// 返回集合的大小(表示最后得到的无重复字符子串长度)
// ❌ 但注意:这样只能得到“最后一次集合”的大小,不一定是最长的子串
return temp.size();
}
# 示例:最长子序列 通过版 好理解
public int lengthOfLongestSubstring(String s) {
// 将字符串转换成字符数组,方便逐个处理
char[] chars = s.toCharArray();
// 定义最大长度,初始值为 1(因为至少有一个字符)
int max = 1;
// 特殊情况:如果字符串长度为 0,直接返回 0
if (0 == chars.length) {
return 0;
}
// 用 ArrayList 来存储当前窗口中的字符(窗口保证无重复)
//而且这里一定不能用 hashSet, 因为它会自动排序。
ArrayList<Character> temp= new ArrayList<>();
// 先把第一个字符加入窗口
temp.add(chars[0]);
// 从第二个字符开始遍历
for (int i = 1; i < chars.length; ) {
// 如果当前字符没有在窗口中出现
if (!temp.contains(chars[i])) {
// 将该字符加入窗口
temp.add(chars[i]);
// 移动右指针 i,继续处理下一个字符
i++;
// 更新最长子串长度
max = Math.max(max, temp.size());
} else {
// 如果当前字符已经在窗口中出现(发生重复)
// 就从窗口左边移除一个字符,相当于滑动窗口的左指针右移一位
temp.remove(0);
// 注意这里没有移动 i,因为要继续检查当前的 chars[i]
// 直到重复字符被移除,才能把它加进去
}
}
// 返回最长子串的长度
return max;
}
复盘总结
易错点:
1.以下是代码语义版思路:
特判
- 把字符串转成字符数组。
- 如果长度为 0,直接返回 0。
初始化
- 设
max = 1(至少有一个字符时,答案不会小于 1)。 - 新建一个“窗口”列表,用来装当前不含重复字符的子串。
- 先把第一个字符放进窗口。
- 右指针
i从第二个字符的位置开始(i = 1)。
主循环(当 i < 字符串长度 时重复执行)
- 看第
i个字符是否已经在窗口里:
情况一:不在窗口里
- 把它加入窗口;
i右移一位(因为这个字符已被成功纳入窗口);- 用当前窗口大小更新
max(max = max(max, 窗口大小))。
情况二:在窗口里(发生重复)
- 从窗口左侧移除一个字符(即移除当前窗口的第一个元素);
- 注意:此时不移动
i,因为还没把第i个字符成功放进来; - 这样做的含义是:不断收缩左边界,直到把与第
i个字符相同的旧字符移出窗口,再尝试把第i个字符放进来。
循环结束与返回
- 当右指针
i走到字符串末尾(i == 长度),说明所有字符都处理完; - 返回记录到的
max,它就是“无重复字符的最长子串”的长度。
2.这个temp临时集合一定不能是HashSet,因为HashSet会自动排序,会导致进去的元素,失去原来的顺序,导致算法失效
3.这个题目一定要想到记录最大值啊,这个值才是返回的答案!! 和什么字符串序列是什么无所谓。
复杂度分析:
- 时间复杂度: O(n)
- 空间复杂度: O(1)
相似题目:
- 指针类、hash类
进阶思考:
- 无
扩展建议
提示:可配合GitHub仓库同步代码,利用提交记录跟踪进步轨迹。

1007

被折叠的 条评论
为什么被折叠?



