无重复字符的最长子串(剑指 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;
    }
}
posted @ 2023-03-08 14:37  忧愁的chafry  阅读(32)  评论(0)    收藏  举报