leetcode-数组系列算法总结-java版本

[仅个人学习使用]

文章目录

  • 题目所属分类
  • 一、leetcode 704二分查找
  • 原题链接
    • 题解
  • 二、leetcode 35. 搜索插入位置
    • 原题
    • 题解1
      • 第二种解法2
  • 三、[leetcode27. 移除元素](https://leetcode.cn/problems/remove-element/)
    • 题解
  • 四、 [leetcode 977.有序数组的平方](https://leetcode.cn/problems/squares-of-a-sorted-array/)
    • 题解
  • 五、[leetcode 209. 长度最小的子数组](https://leetcode.cn/problems/minimum-size-subarray-sum/)
    • 题解
      • 解法1 O(n)
      • 解法2 前缀和 + 双指针 时间复杂度 O(n)
      • 解法3 前缀和 + 二分 时间复杂度 O(nlogn)
  • 六、leetcode 76. 最小覆盖子串
    • 题解
  • 七、leetocde 904. 水果成篮
    • 题解
  • 八、leetocde 59. 螺旋矩阵 II
    • 题解
  • 九、[leetcode 3. 无重复字符的最长子串](https://leetcode.cn/problems/longest-substring-without-repeating-characters/)
    • 题解

题目所属分类

摘抄代码随想录中的算法总结 从leetcode中截取部分为数组一类的算法题合集 此部分为数组系列
包括动态窗口 双指针加哈希表 二分查找模板 共九道题

一、leetcode 704二分查找

原题链接

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

代码案例1:输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

代码案例2:输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

注:你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。

题解

这个题就是二分模板的一个模板题
想看详情可以点击这里 !!!!数的范围-二分算法-java实现!!!!!

class Solution {
    public int search(int[] nums, int target) {
        int l = 0 ; 
        int r = nums.length - 1;
         
        while(l < r){
            int mid = (l+r) >> 1 ;
            if(nums[mid] >= target) r = mid ;
            else l = mid + 1 ;
        }
        if(nums[l] != target) return -1 ;
        return l ;//这里面写l 和r都可以
    }
}

二、leetcode 35. 搜索插入位置

原题

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

输入: nums = [1,3,5,6], target = 5
输出: 2
输入: nums = [1,3,5,6], target = 2
输出: 1
输入: nums = [1,3,5,6], target = 7
输出: 4

题解1

找到第一个大于等于target的位置 如果所有的数都小于target 那么应该插入到最后 所以l和r的范围 应该是0 - Nums.legnth

class Solution {
    public int searchInsert(int[] nums, int target) {        
        int l = 0,r = nums.length  ;
        while(l < r)
        {
            int mid = l + r   >> 1;
            if(nums[mid] >= target) r = mid ;
            else l = mid + 1;
        }
              return l;


    }
}

第二种解法2

这样改边界值这种的话 需要特判一下

class Solution {
    public int searchInsert(int[] nums, int target) {
        int l = 0, r = nums.length - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        if (nums[l] < target) {
            return l + 1;
        }
        return l;
    }
}

 

这种解法是通过二分找到 小于target的 最大的元素的位置,返回该位置的下一个位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int l = -1,r = n - 1;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(nums[mid] < target) l = mid ;
            else r = mid - 1;
        }
        return l + 1;
    }
}

 

三、leetcode27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

题解

相等的就减掉

  • 枚举每个元素,若当前元素与val不一致,则保存该元素
class Solution {
    public int removeElement(int[] nums, int val) {
        int k = 0 ;//返回新数组的长度
        for(int i = 0 ; i < nums.length ; i++){
             if(nums[i] != val){
                 nums[k++] = nums[i];
             }
        }
        return k;
    }
}

也可以按照相等的来 但是这样显然没有直接按照不等的好

class Solution {
    public int removeElement(int[] nums, int val) {
        int j = nums.length - 1;
        for (int i = 0; i <= j; i++) {
            if (nums[i] == val) {
                swap(nums, i--, j--);
            }
        }
        return j + 1;
    }
    void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

 

四、 leetcode 977.有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

注意:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序
  • 请你设计时间复杂度为 **O(n)**的算法解决本问题

题解

蠢办法的话
这样的话是nlogn

class Solution {
    public int[] sortedSquares(int[] nums) {
        int k = 0 ; 
        for(int i = 0 ; i < nums.length ; i++){
            nums[k++] = nums[i]* nums[i] ;
        }
        Arrays.sort(nums);
        return nums;
    }
}

可以按照归并的方法来 就是类似归并排序的算法
我们可以使用两个指针分别指向位置 0 和n−1,每次比较两个指针对应的数,选择较大的那个逆序放入答案并移动指针。这种方法无需处理某一指针移动至边界的情况,
最主要的是研究负数平方后 数组的顺序就需要重新排序 而最大的和最小的只能出现在数组的两边 而无法用k=0开始 因为平方后最小的数没在两边 在数组的中间位置

class Solution {
    public int[] sortedSquares(int[] nums) {
         int n = nums.length;
         int[] res = new int[n];
        for(int i = 0 , j = n -1 , k = n-1  ; i <= j ;){
            if(  nums[i]* nums[i] > nums[j]* nums[j]){
                res[k] = nums[i]* nums[i]; k--;i++;
            } else {
                     res[k] = nums[j]* nums[j];k--;j--;
            }
        }
        
        return res;
    }
}

五、leetcode 209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例1
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例2
输入:target = 4, nums = [1,4,4]
输出:1
示例3:输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
注意:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

  • 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

题解

解法1 O(n)

leetcode-数组系列算法总结-java版本_第1张图片

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int ans = Integer.MAX_VALUE;
        for(int i = 0 , j = 0 , sum = 0 ; i < nums.length ; i++){
            sum += nums[i] ;
            //while循环就是相互去试
            while(sum - nums[j] >= target) sum -= nums[j++];
            if(sum >=  target) ans = Math.min(ans , i - j +1);


        }
        if(ans == Integer.MAX_VALUE) ans = 0 ;
        return ans ;
    }
}

改进 改成前缀和 不过前缀和的话 空间复杂度会多出一个O(n)

解法2 前缀和 + 双指针 时间复杂度 O(n)

前缀和模板

for (int i = 1; i <= n; i ++ ){
	cin >> a[i];
	s[i] = s[i - 1] + a[i];
}

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
         int n = nums.length;
        int[] f = new int[n + 1];
        for(int i = 1;i <= n;i ++) f[i] = f[i - 1] + nums[i - 1];

        int ans = Integer.MAX_VALUE;
        for(int j = 1, i = 0;j <= n;j ++)
        {
            while(f[j] - f[i] >= s) 
            {
                ans = Math.min(ans, j - i);
                i ++;
            }
        }        
        if(ans == Integer.MAX_VALUE) ans = 0 ;
        return ans ;
    }
}

解法3 前缀和 + 二分 时间复杂度 O(nlogn)

1、计算出数字的前缀和f[],即找到f[j] - f[i] >= s 的最小连续长度j - i
2、枚举i位置,固定i位置,在后面的元素中找出f[j] - f[i] >= s 最小的j位置,则对应的长度j - i就是当前位置i的最小连续长度
3、由于f[]数组是单调的,因此在区间[i + 1, n]找最小的f[j],由于可能会存在[i + 1, n]区间中都不存在一个数j,使得f[j] - f[i] >= s,为了方便区分存不存在,因此左右边界开始设置为[i, n + 1],当最后落在i或者n + 1时,则表示不存在f[j]这个数

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int n = nums.length;
        int[] f = new int[n + 1];
        for(int i = 1;i <= n;i ++) f[i] = f[i - 1] + nums[i - 1];

        int ans = Integer.MAX_VALUE;
        for(int i = 0;i < n;i ++){
            int l = i, r = n + 1;
            while(l < r){           
                int mid = l + r >> 1;
                if(f[mid] - f[i] >= s) r = mid;
                else l = mid + 1;
            }
            if(r >= i + 1 && r <= n) ans = Math.min(ans, r - i);
        }
        if(ans == Integer.MAX_VALUE) return 0;
        return ans;
    }
}

 

六、leetcode 76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例1 :
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
示例2:输入:s = “a”, t = “a”
输出:“a”
示例3:输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

1 <= s.length, t.length <= 105
s 和 t 由英文字母组成
进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

题解

双指针算法的 i j 条件就是前面的j 往后走 i 也往后走 单调才可以使用双指针算法 或者叫滑动窗口算法
1、cnt维护的是s字符串[j,i]区间中满足t字符串的元素的个数(此题的主要精髓)
2、hs哈希表维护的是[j,i]区间中各个字符出现多少次,ht哈希表维护的是t字符串各个字符出现多少次
3、枚举过程中,将s[i]加入到hs中,同时观察s[j]是否多余,若多余则移出
4、将s[i]加入到hs中,若hs[i] <= ht[i],表示多了一个符号的元素,cnt ++
5、若当前双指针维护的窗口满足个数要求,则更新答案
java写起来确实很费力 注意的是边界问题 t可能为0 的情况

// 有效字符数cnt == t.size() 后 res 赋值的情况有两种:
// 1. res为空时, 是 第 1 次赋值
// 2. res.size() 变小后, 是 第 2, 3, 4, 5....次赋值
if (cnt == t.size())
    if (res.empty() || i - j + 1 < res.size())
        res = s.substr(j, i - j + 1);

简洁版

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> hs, ht;
        for (auto c: t) ht[c] ++ ;

        string res;
        int cnt = 0;
        for (int i = 0, j = 0; i < s.size(); i ++ ) {
            hs[s[i]] ++ ;
            if (hs[s[i]] <= ht[s[i]]) cnt ++ ;

            while (hs[s[j]] > ht[s[j]]) hs[s[j ++ ]] -- ;
            if (cnt == t.size()) {
                if (res.empty() || i - j + 1 < res.size())
                    res = s.substr(j, i - j + 1);
            }
        }

        return res;
    }
};

注释版

// 双指针 滑动窗口 要满足单调性。即 i, j 指针 均只能 朝着 一个方向移动, 不能朝一个方向移动 再 向另一个方向移动.
// 设滑动窗口 s[j ~ i], 设 指针 i 为 滑动窗口的 终点, 指针 j 为 滑动窗口的 起点.
// 设 哈希表 hw(hash_window) 存的是 滑动窗口内 各字符的数量, ht存的是 字符串t内各字符数量.
//
// 问题 1 :如何 快速判断 滑动窗口 s[j ~ i] 内 是否 已经包含了 t内所有字符了呢(每种字符可能重复)?
// 滑动窗口 内的 有效字符 总数量 cnt = t.size(), 就说明已经包含了 t 内所有字符.
// 问题 2 :那么 什么是 有效字符呢?
// 如果 此时 新加入了 s[i]字符之后, hw[s[i]] <= ht[s[i]], 此时的字符 s[i]有用, 就算有效字符, cnt ++.
// 如果 新加入了 s[i]字符之后, 有 hw[s[i]] > ht[s[i]] 了, 说明加入当前字符s[i] 之前, 滑动窗口内 s[i]的数量
// --- 就已经 达到了 t内这个字符s[i]的数量, 原本关于 字符s[i] 就已经达标, 现在再加就是冗余, 就是无效字符.
//
// 问题 3 :滑动窗口 s[j ~ i]的 起点 指针 j 什么时候 才 向右 移动?
// 如果 s[j]处的字符 (字符记为#) 的数量 大于 t 中 这个字符 # 的数量, 即 hw[s[j]] > ht[s[j]],
// --- j 就可以向右移动, 因为 s[j] 处的字符 # 的数量 冗余了, j 向右移动的时候, 同时 将 hw[s[j]] --

// 问题 4 : 有效字符的数量 cnt 变化是靠的什么呢?
// cnt ++ 只能通过 指针 i 向右移动, 但是 注意 指针 i  不一定 每次 向右移动 都会 使有效字符 cnt ++。
// 指针 j 向右移动 不会影响 有效字符cnt 的数量, 即 虽然不会 cnt ++, 但是也不会出现 导致cnt --.
//
// 问题 5 : 指针 i 和 指针 j 的作用, 这里 打个 形象的比喻 ~~~
// 指针 i 的作用是 开源,负责 增加有效字符cnt 的数量, 开疆破土, 助力 有效字符数量 cnt 早日达到 t.size(); 
// 指针 j 的作用是 节流,负责 将 指针 i 开辟的疆土 中 无价值 的部分 即 冗余的字符 回收,达到 最小 覆盖子串的目的。
//
// 问题 6 :双指针工作的流程 是 怎样的呢? 明白了 指针 j 和 i 的作用 就很好理解啦 ~ 
// (1) 肯定是 指针 i 先移动, hw[s[i]] ++, 然后看看 是否 会 增加 cnt, 通过 if (hw[s[i]] <= ht[s[i]]) 来判断
// (2) i 每次向右移动一步之后, 每次 i 移动之后 都可能会导致 cnt == t.size(), 因为题目是求 最小 覆盖子串, 所以
// --- 在 判断 if (cnt == t.size()) 之前 要 先 用 j 将 无用的 字符回收, 达到 最小覆盖子串 的目的
// --- j 就负责 看看 i 之前开辟的疆土里面有没有 冗余的字符, 有的话就回收, hw[s[j]] -- 同时 j ++ 
// (3) i 每次开辟完新的疆土 同时 j 也回收了 可能存在的 无价值的 冗余字符, 就可以判断 cnt == t.size()啦 ~~ 

class Solution {
public:
    string minWindow(string s, string t) {
        // hw 存的是 滑动窗口内 各字符数量, ht 存的是 字符串t 的 各字符数量
        unordered_map<char, int> hw, ht; // 也可开一个哈希表作差,但是开两个思路更清晰
        for (auto c: t) ht[c] ++ ; // 统计 字符串 t 中 各字符 数量

        string res;

        int cnt = 0; // 注意: cnt 中 存的是 有效字符的数量

        for (int i = 0, j = 0; i < s.size(); i ++ ) {
            // (1) 指针 i 先移动, hw[s[i]] ++, 然后看看 是否 会 增加 cnt, 通过 if (hw[s[i]] <= ht[s[i]]) 来判断
            hw[s[i]] ++ ;
            if (hw[s[i]] <= ht[s[i]]) cnt ++ ;

            // (2) j 就负责 看看 i 之前开辟的疆土里面有没有 冗余的字符, 有的话就回收, hw[s[j]] -- 同时 j ++ 
            // 每次 i 移动完 之后, 判断 j 能不能向右移动, 注意 j 可以连续移动, 用while 不是 if
            // while (hw[s[j]] > ht[s[j]]) hw[s[j ++ ]] -- ; // 是 while, 不是 if
             while (j <= i && hw[s[j]] > ht[s[j]]) hw[s[j ++ ]] -- ; // 加上 j <= i,防止字符串下标越界

            // (3) i 每次开辟完新的疆土 同时 j 也回收了 可能存在的 无价值的 冗余字符, 就可以判断 cnt == t.size()啦
            if (cnt == t.size()) { // 更新答案
                if (res.empty() || i - j + 1 < res.size()) // 注意 res.empty()
                    res = s.substr(j, i - j + 1);
            }
        }

        return res;
    }
};

 
class Solution {
    public String minWindow(String s, String t) {
        HashMap<Character,Integer> hs = new HashMap<Character,Integer>();//存s字符串的哈希表 滑动窗口
        HashMap<Character,Integer> ht = new HashMap<Character,Integer>();//存t字符串的哈希表
        for(char a : t.toCharArray() ) {//向哈希表ht中计数
            ht.put(a,ht.getOrDefault(a,0)+1);       
        }
        String res = "" ;
        int cnt = 0 ; 
        for(int i = 0, j = 0  ; i < s.length() ; i++){
            hs.put(s.charAt(i),hs.getOrDefault(s.charAt(i),0)+1);//向哈希表hs中计数
            //滑动窗口向右移动的时候 多加入一个字符 使得hs小与等于ht里面的字符个数 这样的叫做有效字符
         if(  hs.get(s.charAt(i)) <= ht.getOrDefault(s.charAt(i),0)) cnt ++;//有效计数加一
          // if( ht.containsKey(s.charAt(i)) && hs.get(s.charAt(i)) <= ht.get(s.charAt(i))) cnt ++;
            //开始滑动窗口左边的j移动 反过来的话 滑动窗口就需要移动了 记得此时是j向右移动
            while( j < i && hs.get(s.charAt(j)) > ht.getOrDefault(s.charAt(j),0)) {
            //加上 j <= i,防止字符串下标越界
                 hs.put( s.charAt(j) ,hs.get(s.charAt(j))-1);j++;
            }
           
               
            if(cnt == t.length()){
                if(res.isEmpty() || i - j + 1 < res.length() )
                 res =  s.substring(j , i + 1);
            }

        }
        return res ;
    }
}

七、leetocde 904. 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

  • 1 <= fruits.length <= 105
  • 0 <= fruits[i] < fruits.length

题解

这题就是在寻找 序列中长度最长的 只包含两个元素的 区间的长度

class Solution {
    public int totalFruit(int[] fruits) {
        int res =0 ; 
        HashMap<Integer,Integer> cnt = new HashMap<>();
        //s表示哈希表中有多少个不同元素
        for(int i =0 , j = 0 , s = 0 ; i < fruits.length ; i++){
            cnt.put(fruits[i],cnt.getOrDefault(fruits[i],0)+1);//先放哈希表中
            if(cnt.get(fruits[i]) == 1 )s++;//说明当前元素是一个新的元素 种类加一
            while(s > 2){
                //此时种类要是超于2  j就往后走 所以s就会被减去
                 cnt.put(fruits[j],cnt.get(fruits[j])-1);
                if(cnt.get(fruits[j]) == 0 ) s--;
                j++;
            }
            res = Math.max(res,i - j + 1);

        }
        return res ;
    }
}

八、leetocde 59. 螺旋矩阵 II

leetcode-数组系列算法总结-java版本_第2张图片

  • 1 <= n <= 20

题解

走法是右下左上 按照的是
leetcode-数组系列算法总结-java版本_第3张图片

从左上角开始遍历,先往右走,走到不能走为止,然后更改到下个方向,再走到不能走为止,依次类推,遍历 n2n2 个格子后停止。

时间复杂度分析:矩阵中每个格子遍历一次,所以总时间复杂度是 O(n2) 。

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] ans = new int[n][n];
        
        int dx[] = {0, 1, 0, -1};//右下左上
        int dy[] = {1, 0, -1, 0};

        for (int x = 0, y = 0, i = 1, d = 0; i <= n * n; i ++ )//d表示方向
        {
            ans[x][y] = i ;
            int a = x + dx[d], b = y + dy[d];
            if (a < 0 || a >= n || b < 0 || b >= n || ans[a][b] != 0)//越界 或者说这个格子已经填数了
            {
                d = (d + 1) % 4;
                a = x + dx[d]; b = y + dy[d];
            }
            x = a; y = b;
        }

        return ans;
    }

 
    }

九、leetcode 3. 无重复字符的最长子串

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

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

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

题解

一道经典的双指针算法的题

class Solution {
    public int lengthOfLongestSubstring(String s) {
         HashMap<Character,Integer> map = new HashMap<>();
        if(s.length() == 0) return 0;
        int ans = 0 ;
        for(int i = 0 ,j = 0 ; i < s.length() ; i++){
            map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1); //把s放入哈希表中
            while(map.get(s.charAt(i)) > 1){//判断是否有重复元素 关于j++的都是循环起步
               //对哈希表进行操作
                map.put(s.charAt(j),map.get(s.charAt(j))-1);
                j++;
            }
            ans = Math.max(ans,i - j + 1);
        }
        return ans ;
    }
}

你可能感兴趣的:(leetcode算法总结,算法,leetcode,数据结构)