文章目录
- 一、题目描述
- 二、问题分析
- 三、解法一:暴力枚举法
- 四、解法二:滑动窗口(推荐)
- 五、复杂度分析
- [六、Java 实现代码(滑动窗口)](#六、Java 实现代码(滑动窗口))
- 七、补充:使用数组优化
- 总结
一、题目描述
给定一个字符串 s ,请你找出其中 不含重复字符 的 最长子串 的长度。
示例:
输入: s = "abcabcbb"
输出: 3
解释: 最长子串是 "abc",长度为 3。
二、问题分析
这道题要求找出 最长的无重复字符的连续子串 。
可以想到用 暴力枚举法 或 滑动窗口法:
- 暴力:枚举所有子串 → 检查是否重复 → 找最长长度。
- 滑动窗口:用双指针维护一段不重复的区间 → 动态右扩,必要时左缩。
显然,滑动窗口是最优解法,因为它避免了重复检查。
三、解法一:暴力枚举法
思路
- 枚举所有可能的子串起点和终点。
- 判断该子串中是否有重复字符。
- 记录最长长度。
流程图(暴力枚举)
无重复
有重复
开始
初始化 maxLen = 0
遍历左指针 i from 0..n-1
遍历右指针 j from i..n-1
获取 s[i..j]
判断是否有重复字符
更新 maxLen
继续遍历
结束
返回 maxLen
时间复杂度分析
- 时间复杂度: O(n³) (枚举所有子串 + 检查重复)
- 空间复杂度: O(n)
不适合大数据量。
四、解法二:滑动窗口(推荐)
思路说明
滑动窗口法通过维护一段 无重复的子串区域 来动态更新结果:
- 使用两个指针
left和right表示窗口边界。 - 使用一个
HashSet或HashMap记录窗口中字符出现位置。 - 每次右指针向右移动:
- 若新字符不存在于窗口,则加入并更新长度。
- 若新字符已出现,则左指针右移到出现位置之后。
动态示意图
假设输入 s = "abcabcbb"
| 步骤 | 右指针字符 | 当前窗口 | 最大长度 |
|---|---|---|---|
| 1 | a | a | 1 |
| 2 | b | ab | 2 |
| 3 | c | abc | 3 |
| 4 | a | bca | 3 |
| 5 | b | cab | 3 |
| 6 | c | abc | 3 |
最终结果为 3。
流程图(滑动窗口)
渲染错误: Mermaid 渲染失败: Parse error on line 5: ...|重复| E[更新 left = max(left, map[char] + 1 -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
五、复杂度分析
| 项目 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 暴力枚举 | O(n³) | O(n) |
| 滑动窗口 | O(n) | O(k),其中 k 为字符集大小 |
滑动窗口显然更优。
六、Java 实现代码(滑动窗口)
java
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int left = 0, maxLen = 0;
Map<Character, Integer> map = new HashMap<>();
for (int right = 0; right < n; right++) {
char c = s.charAt(right);
if (map.containsKey(c)) {
// 更新左指针到重复字符之后位置
left = Math.max(left, map.get(c) + 1);
}
map.put(c, right);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
}
七、补充:使用数组优化
若字符集已知(如仅英文字符),可用数组代替 Map:
java
class Solution {
public int lengthOfLongestSubstring(String s) {
int[] index = new int[128]; // ASCII
int left = 0, maxLen = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
left = Math.max(index[c], left);
maxLen = Math.max(maxLen, right - left + 1);
index[c] = right + 1;
}
return maxLen;
}
}
总结
| 方法 | 核心思路 | 时间复杂度 | 是否推荐 |
|---|---|---|---|
| 暴力法 | 枚举所有子串 + 检查重复 | O(n³) | 否 |
| 滑动窗口 + HashMap | 动态维护无重复窗口 | O(n) | ✅ 推荐 |
| 滑动窗口 + 数组优化 | 改进存储结构 | O(n) | ✅ 推荐 |