前言
今天写了一道题 LeetCode 3. 无重复字符的最长子串,思路还是不难的。初始版本自己写的,看了别人的答案觉得自己写的太丑了。于是乎,借鉴《重构》的思想(LeetCode 提供测试用例和运行,保障重构安全,真是太好了),把自己的版本重构下,记录下过程。
初始版本
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int max = 0;
int slow = 0;
int fast = 0;
Set<Character> set = new HashSet<>();
// 根据fast重置slow节点
while (slow < arr.length) {
if (! set.contains(arr[fast])) {
set.add(arr[fast]);
max = Math.max(max, set.size());
fast++;
if (fast == arr.length) {
return max;
}
} else {
max = Math.max(max, set.size());
set.clear();
slow++;
// 从slow的起点开始重新寻找
fast = slow;
}
}
return max;
}
优化遍历次数
摘一个 LeetCode网友的评论
每次左指针右移一位,移除set的一个字符,这一步会导致很多无用的循环。while循环发现的重复字符不一定就是Set最早添加那个,还要好多次循环才能到达,这些都是无效循环,不如直接用map记下每个字符的索引,直接进行跳转
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int max = 0;
int slow = 0;
int fast = 0;
// 记录某个字符最接近队尾的标记,用于优化循环次数
HashMap<Character, Integer> map = new HashMap<>();
while (slow < arr.length) {
if (! map.containsKey(arr[fast])) {
map.put(arr[fast], fast + 1);
max = Math.max(max, map.size());
// 继续向后探索是否有更多不重字符
fast++;
// 越界处理
if (fast == arr.length) {
return max;
}
} else {
max = Math.max(max, map.size());
slow = map.get(arr[fast]);
fast = slow;
map.clear();
}
}
return max;
}
优化代码结构
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int max = 0;
int slow = 0;
int fast = 0;
// 记录某个字符最接近队尾的标记,用于优化循环次数
HashMap<Character, Integer> map = new HashMap<>();
while (slow < arr.length) {
if (! map.containsKey(arr[fast])) {
map.put(arr[fast], fast + 1);
max = Math.max(max, map.size());
// 继续向后探索是否有更多不重字符
fast++;
// 越界处理
if (fast == arr.length) {
return max;
}
} else {
max = Math.max(max, map.size());
slow = map.get(arr[fast]);
fast = slow;
map.clear();
}
}
return max;
}
调整代码结构 - 分支结构重复的提到外层
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int max = 0;
int slow = 0;
int fast = 0;
// 记录某个字符最接近队尾的标记,用于优化循环次数
HashMap<Character, Integer> map = new HashMap<>();
while (slow < arr.length) {
if (! map.containsKey(arr[fast])) {
map.put(arr[fast], fast + 1);
// 继续向后探索是否有更多不重字符
fast++;
} else {
slow = map.get(arr[fast]);
fast = slow;
map.clear();
}
max = Math.max(max, map.size());
// 越界处理
if (fast == arr.length) {
return max;
}
}
return max;
}
调整代码结构 – 内存优化、循环条件
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int max = 0;
int slow = 0;
int fast = 0;
// 记录某个字符最接近队尾的标记,用于优化循环次数
HashMap<Character, Integer> map = new HashMap<>();
// 越界条件移到while上
while (slow < arr.length && fast < arr.length) {
if (! map.containsKey(arr[fast])) {
map.put(arr[fast], fast + 1);
// 继续向后探索是否有更多不重字符
fast++;
} else {
slow = map.get(arr[fast]);
fast = slow;
// 用 new 表达一个全新的map
map = new HashMap<>();
}
max = Math.max(max, map.size());
}
return max;
}
调整代码结构 – 反转 ! xxx 条件
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int max = 0;
int slow = 0;
int fast = 0;
HashMap<Character, Integer> map = new HashMap<>();
while (slow < arr.length && fast < arr.length) {
if (map.containsKey(arr[fast])) {
slow = map.get(arr[fast]);
fast = slow;
map = new HashMap<>();
} else {
map.put(arr[fast], fast + 1);
fast++;
}
max = Math.max(max, map.size());
}
return max;
}
后记
时常提醒自己,写算法题的时候,不要急功近利。通过迭代答案,自己的答案也会变得简洁。