LeetCode hot 100 (8-11,自用2026.04.03)

LeetCode hot 100 (8-11)

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

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

示例 1:

复制代码
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。
  • s 由英文字母、数字、符号和空格组成

思路

一般出现散列表,都是字符作为键,出现的次数作为值

涉及子串,考虑滑动窗口

要记录下标,然后遇到同样的字符的时候往右划窗口,刚好不包含那个重复的元素

java 复制代码
//外层循环扩展右边界,内层循环扩展左边界
for (int l = 0, r = 0 ; r < n ; r++) {
	//当前考虑的元素
	while (l <= r && check()) {//区间[left,right]不符合题意
        //扩展左边界
    }
    //区间[left,right]符合题意,统计相关信息
}

题解

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        char[] ss=s.toCharArray();
        Set<Character> set=new HashSet<>();
        int res=0;
        int n=s.length();
        for(int l=0,r=0;r<n;r++){
            char ch=ss[r];
            while(set.contains(ch)){
                set.remove(ss[l]);
                l++;
            }
            set.add(ss[r]);
            res=Math.max(res,r-l+1);
        }
        return res;
    }
}

438. 找到字符串中所有字母异位词

给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

示例 1:

复制代码
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

思路

p的长度肯定是s子串的长度,子串的顺序固定了的话,那就从头扫到尾,因为只包含小写字母,维护两个26大小的数组就行

补充:出现次数也要一致,是定长滑动窗口

只需要计数,统计两个数组相同是Arrays.equals(数组1,数组2)

题解

java 复制代码
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(),pLen=p.length();
        if(sLen<pLen){
            return new ArrayList<Integer>();
        }
        List<Integer> res = new ArrayList<>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for(int i=0;i<pLen;++i){
            ++sCount[s.charAt(i)-'a'];
            ++pCount[p.charAt(i)-'a'];
        }
        if(Arrays.equals(sCount,pCount)){
            res.add(0);
        }
        for(int i=0;i<sLen-pLen;++i){
            --sCount[s.charAt(i)-'a'];
            ++sCount[s.charAt(i+pLen)-'a'];
            if(Arrays.equals(sCount,pCount)){
                res.add(i+1);
            }
        }
        return res;
    }
}

优化思路

复制代码
Arrays.equals(sCount,pCount)在比较两个数组
优化之后,只用一个数组
count[x] = 当前窗口中字符x的个数 - p中字符x的个数

differ = 有多少个字符的 count[x] != 0
也就是differ == 0时就是异位词

这样每次窗口移动,只需要关心:
出去的那个字符
进来的那个字符
java 复制代码
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();

        if (sLen < pLen) {
            return new ArrayList<Integer>();
        }

        List<Integer> ans = new ArrayList<Integer>();
        int[] count = new int[26];
        for (int i = 0; i < pLen; ++i) {
            ++count[s.charAt(i) - 'a'];
            --count[p.charAt(i) - 'a'];
        }

        int differ = 0;
        for (int j = 0; j < 26; ++j) {
            if (count[j] != 0) {
                ++differ;
            }
        }

        if (differ == 0) {
            ans.add(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            if (count[s.charAt(i) - 'a'] == 1) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
                --differ;
            } else if (count[s.charAt(i) - 'a'] == 0) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
                ++differ;
            }
            --count[s.charAt(i) - 'a'];

            if (count[s.charAt(i + pLen) - 'a'] == -1) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
                --differ;
            } else if (count[s.charAt(i + pLen) - 'a'] == 0) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
                ++differ;
            }
            ++count[s.charAt(i + pLen) - 'a'];
            
            if (differ == 0) {
                ans.add(i + 1);
            }
        }

        return ans;
    }
}

560. 和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。

子数组是数组中元素的连续非空序列。

示例 1:

复制代码
输入:nums = [1,1,1], k = 2
输出:2

思路

复制代码
排序,然后左右同时扫,大了的话就左移动,小了的话就右移动;
判断左是否比总和大,如果大就结束;判断右是否比总和小,小的话也结束;
这是两个元素的,三个元素呢......

上面的思路有问题,不能排序!没有单调的话双指针也失效了

标准做法是:前缀和 + 哈希表

题解

java 复制代码
class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 1); // 前缀和为0,出现1次
        int sum = 0;
        int ans = 0;
        for (int num : nums) {
            sum += num;
            if (map.containsKey(sum - k)) {
                ans += map.get(sum - k);
            }
            map.put(sum, map.getOrDefault(sum, 0) + 1);
        }
        return ans;
    }
}

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

复制代码
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

思路

定长滑动窗口,最差的情况就是左右指针for然后比较里面的内容

单调队列

题解

java 复制代码
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // 数组长度
        int n =nums.length;
        // 定义单调队列,队列里存储的是索引。
        Deque<Integer> deque = new LinkedList<>();
        // 初始化窗口,也就是完整导入前k个数字
        for(int i=0;i<k;++i){
            // 队列为不空,且新的数字比队列末尾数字大的时候
            while(!deque.isEmpty()&&nums[i]>=nums[deque.peekLast()]){
                // 弹出末尾元素
                deque.pollLast();
            }
            // 从队尾加新元素上去
            deque.offerLast(i);
        }
        // 定义最后返回数组,大小是n-k+1
        int[] ans =new int[n-k+1];
        // 第一个元素是队头的元素
        ans[0] = nums[deque.peekFirst()];
        // 从位置k开始滑动,直到小于n
        for(int i=k;i<n;++i){
            // 队列不空,且新的数字比队尾大
            while(!deque.isEmpty()&&nums[i]>=nums[deque.peekLast()]){
                // 从队尾弹出
                deque.pollLast();
            }
            // 从队尾加入新元素
            deque.offerLast(i);
            // 判断队头还在不在窗口内,不在的话就从前面弹出
            while(deque.peekFirst()<=i-k){
                deque.pollFirst();
            }
            // 存入当前窗口最大值,索引是deque.peekFirst,也就是队头元素
            ans[i-k+1]=nums[deque.peekFirst()];
        }
        return ans;
    }
}
相关推荐
Q741_1472 小时前
每日一题 力扣 3661. 可以被机器人摧毁的最大墙壁数目 双指针 动态规划 C++ 题解
c++·算法·leetcode·机器人·动态规划
alphaTao2 小时前
LeetCode 每日一题 2026/3/30-2026/4/5
算法·leetcode·职场和发展
圣保罗的大教堂11 小时前
leetcode 3418. 机器人可以获得的最大金币数 中等
leetcode
_日拱一卒15 小时前
LeetCode:最大子数组和
数据结构·算法·leetcode
小辉同志17 小时前
78. 子集
算法·leetcode·深度优先
Demon--hx20 小时前
[LeetCode]100 链表-专题
算法·leetcode·链表
_深海凉_20 小时前
LeetCode热题100-LRU 缓存
算法·leetcode·缓存
liuyao_xianhui21 小时前
优选算法_岛屿的最大面积_floodfill算法_C++
java·开发语言·数据结构·c++·算法·leetcode·链表