Leetcode3. 无重复字符的最长子串

230 阅读2分钟

Leetcode3. 无重复字符的最长子串

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

❤️‍欢迎订阅java厂长《LeetCode每日一题》 ❤️‍

1、题目📑

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

实例1

输入: s = "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

实例2

输入: s = "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

实例3

输入: s = "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

2、思路🧠

方法一:滑动窗口(利用循环去重)

  1. 定义一个set集合来模拟字符串的窗口,left表示窗口的左端,right表示窗口的右端。
  2. 在移动的过程中保证左端一直在左侧
  3. 如果窗口中包含当前元素,说明出现了重复,把窗口最左端的给移除掉,直到窗口不包含当前元素。
  4. 窗口不出现重复元素,不断右移,记录窗口的最大长度。

方法二:滑动窗口(利用Map去重)

  1. left表示窗口的左端,right表示窗口的右端
  2. 定义一个 map 数据结构存储 (k, v),其中 key 值为窗口右端的字符,value 值为右端字符位置的下一个位置,加 1 表示从字符位置后一个才开始不重复。
  3. 窗口的右端向右移动,在right遍历的过程中,窗口【left,right】会出现相同字符的情形,将字符作为 key 值,获取其 value 值,并更新 left 左端的变化,以保证区间不会出现重复的字符。
  4. 同时使用 maxLength 来记录窗口的最大长度。

废话少说~~~~~上代码!

3、代码👨‍💻

第一次commit AC

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length() == 0) return 0;
        if(s.length() == 1) return 1;
        
        Set<Character> set = new HashSet<>();
        int left = 0, right = 0,maxLength = 0;
        while (right < s.length()) {
            // 如果窗口中包含当前元素,说明出现了重复,
            // 把窗口最左端的给移除掉,直到窗口不包含当前元素即可
            while(set.contains(s.charAt(right))) 
                set.remove(s.charAt(left++));

            //窗口不出现重复元素,右移
                set.add(s.charAt(right++));
            //记录窗口的最大长度
                maxLength = Math.max(maxLength, right - left);
        }
        return maxLength;
    }
}

时间复杂度:O(N) N为字符串 s 的长度

空间复杂度:O(1)

第二次commit AC

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length() == 0) return 0;
        if(s.length() == 1) return 1;
        
        Map<Character, Integer> map = new HashMap<>();
        int left = 0, right = 0,maxLength = 0;
        while (right < s.length()) {
            if(map.containsKey(s.charAt(right))) {
                left = Math.max(left, map.get(s.charAt(right)));
            }
            map.put(s.charAt(right), right + 1);
            maxLength = Math.max(maxLength, right - left + 1);
            right ++;
        }
        return maxLength;
    }
}

时间复杂度:O(N) N为字符串 s 的长度

空间复杂度:O(1)

image-20220506215304490

4、总结

该题目的对大根堆的结构进行灵活的运用,并且能够想到对最大数减半操作收益是最大的。

自建堆模板:

package com.cz.Sort;

import java.util.Arrays;

/**
 * @ProjectName: 大根堆
 * @Package: com.cz.Sort
 * @ClassName: Heap
 * @Author: 张晟睿
 * @Date: 2022/3/17 20:23
 * @Version: 1.0
 */
public class Heap {
    public static class MyMaxheap {
        private int heap[];
        private final int limit;
        private int heapSize;

        public MyMaxheap(int limit) {
            heap = new int [limit];
            this.limit = limit;
            heapSize = 0;
        }

        public boolean isEmpty() {
            return heapSize == 0;
        }

        public boolean isFull() {
            return heapSize == limit;
        }

        public void push(int value){
            if (isFull())
                throw new RuntimeException("heap is full");
            heap[heapSize] = value;
            heapInsert(heap, heapSize++);

        }
        public int pop(){
            int ans = heap[0];
            swap(heap, 0 , --heapSize);
            heapify(heap, 0, heapSize);
            return ans;
        }

        private void heapInsert(int[] arr, int index) {
            //当前index和父亲比较 当前index比其父亲大,交换

            //当前来的值和他的父亲节点比较

            //结束条件
            //arr[index] 不比  arr[index父] 大的时候
            //index = 0 来到了顶端
            while(arr[index] > arr[(index - 1) / 2]) {
                swap(arr, index , (index - 1) / 2);
                index = (index - 1) / 2;
            }
        }

        /**
         *
         * @param arr  需要调整为大根堆的数组
         * @param index 从index位置往下看,不断的下沉   停止条件:当前所有的孩子都不比我大,没有孩子了
         * @param heapSize  堆的大小
         */
        private void heapify(int[] arr, int index, int heapSize) {
            int left = 2 * index ^ 1; //左孩子的下标
            while(left < heapSize) { //左孩子下标不越界,即有孩子存在

                //左右两个孩子,谁大把谁给largest
                //右孩子胜出的情况下 ->1.有右孩子  2.有孩子的值比左孩子的值大
                //否则,就是左孩子胜出
                // 右孩子胜出   left + 1 < heapSize 右孩子不越界     arr[left + 1] > arr[left]  右孩子的值大于左孩子的值
                int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;

                //largest两个孩子中最大孩子的下标
                //最大孩子的值和父亲的值进行pk 谁大把谁的下标给largest
                largest = arr[largest] > arr[index] ? largest : index;
                //两个孩子都pk不过父节点,此时不用下沉了
                if(largest == index) break;
                //largest,index交换
                swap(arr, largest, index);
                //index继续往下沉,继续找下一个左孩子
                index = largest;

                left = (2 * index) ^ 1;
            }
        }

        public void swap(int arr[], int i, int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }

    public static void main(String[] args) {
       MyMaxheap mmp = new  MyMaxheap(10);
//        for (int i = 0; i < 1000; i++) {
//            if (mmp.isFull()) break;
//            mmp.push((int)(Math.random() * 100));
//        }
//        mmp.push(1);
//        mmp.push(3);
//        mmp.push(6);
//        mmp.push(2);
//        mmp.push(5);
//        mmp.push(9);
//        System.out.println(Arrays.toString(mmp.heap));
//        System.out.println(mmp.pop());
        int arr[] = new int[]{1,3,6,2,5,9};
        for (int i = arr.length - 1; i >=  0; i--) {
            mmp.heapify(arr, i , arr.length);
        }
    }
}

❤️‍来自专栏《LeetCode基础算法题》欢迎订阅❤️‍

厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。

对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!

原题链接:3. 无重复字符的最长子串 - 力扣(LeetCode)