无重复字符的最长子串(剑指 Offer II 016. 不含重复字符的最长子字符串 && 3. 无重复字符的最长子串)
题目:

思路:
【1】基于暴力方式,采用双循环的方式【其实可以看做双指针】加上辅助记录空间,但是由于遍历是次方数的所以,其实效率感觉会比较低下,而且每一次遍历都需要开一个辅助空间,所以空间复杂度也高。
【2】进行了一波优化:采用队列的辅助空间,且只遍历一次,没遇到相同的就直接塞入队列,遇到相同的就将队列的数据弹出,直至将相同数据弹出为止。看似空间复杂度还是O(N)【感觉中其实会比上一种节约一点,但其实运行测试时,并没有降低】,而且时间复杂度看似变为了O(N)【但测试的时候并没有减少,估计是队列检测的那部分其实也是用循环的吧】
【3】最后一种,其实是上面两种的结合,采用布尔值数组作为辅助记录空间【其实用int也是可以的,而且相对而言类型值更小】,然后采用双指针。如数据为abcabcbb,那么一个指针优先遍历到下标为3的地方,且辅助空间会对应的abc三个的下标都会变为true,而当下标遇到了a这个重复的时候,由于a在第一个,所以第二个指针会移动一位,指向b,且需要将a的下标置为false,然后在进行常规遍历。【结合代码看更容易理解】【这种优势在空间复杂度为O(1),时间复杂度为O(N),最差是遍历2N次,最优是遍历N次】
代码展示:
开始版本【暴力破解的方式】:
//执行用时:17 ms, 在所有 Java 提交中击败了17.08%的用户 //内存消耗:41.8 MB, 在所有 Java 提交中击败了35.69%的用户 class Solution { public int lengthOfLongestSubstring(String s) { int max = 0, j; for (int i = 0; i < s.length(); i++){ j = i; boolean[] map = new boolean[128]; while (j<s.length()&&!map[s.charAt(j)]){ map[s.charAt(j)] = true; j++; } max = Math.max(max,j-i); } return max; } }
修改版本:
//执行用时:17 ms, 在所有 Java 提交中击败了17.08%的用户 //内存消耗:41.8 MB, 在所有 Java 提交中击败了31.32%的用户 class Solution { public int lengthOfLongestSubstring(String s) { //创建队列 Queue<Character> queue = new LinkedList<>(); int max = 0; for (char c : s.toCharArray()) { while (queue.contains(c)) { //如果有重复的,队头出队,直到没有重复的为止 queue.poll(); } //添加到队尾 queue.add(c); //更新max max = Math.max(max, queue.size()); } return max; } }
最优版本:
//时间2 ms 击败 92.72% //内存42.6 MB 击败 32.7% class Solution { public int lengthOfLongestSubstring(String s) { // 因为出现的字符都是在ASCII里面的故256个标识就可以了 int[] flag = new int[256]; // 其中max是用于记录最大长度 , left代表窗口的左边界 , i为右边界 int left = 0 , max = 0; for (int i = 0; i < s.length(); i++){ flag[s.charAt(i)]++; // 说明有重复的字符,此时需要左边界挪动到重复字符不出现的位置 while (flag[s.charAt(i)] > 1){ flag[s.charAt(left++)]--; } // 记录最大长度 max = Math.max(max,(i-left+1)); } return max; } }
//执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户 //内存消耗:41.3 MB, 在所有 Java 提交中击败了88.01%的用户 class Solution { public int lengthOfLongestSubstring(String s) { //字符不一定全部由字母构成 boolean[] seen = new boolean[128]; //max:最长子串长度 //j:表示不含有重复字符的子串的第一个字符下标 int max = 0, j = 0; for (int i = 0; i < s.length(); i++) { int cInt = s.charAt(i); //当出现了重复的字符,就将j向后推移直到找到第一个重复的字符,然后将之前的变化进行复原 while (seen[cInt]) { seen[s.charAt(j)] = false; j++; } //每次访问到一个不同的字符,将其标记为已经访问 seen[cInt] = true; max = Math.max(max, i - j + 1); } return max; } }

浙公网安备 33010602011771号