1477.找两个和为目标值且不重叠的子数组

目标

给你一个整数数组 arr 和一个整数值 target 。

请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target 。可能会有多种方案,请你返回满足要求的两个子数组长度和的 最小值 。

请返回满足要求的最小长度和,如果无法找到这样的两个子数组,请返回 -1 。

示例 1:

输入:arr = [3,2,2,4,3], target = 3
输出:2
解释:只有两个子数组和为 3 ([3] 和 [3])。它们的长度和为 2 。

示例 2:

输入:arr = [7,3,4,7], target = 7
输出:2
解释:尽管我们有 3 个互不重叠的子数组和为 7 ([7], [3,4] 和 [7]),但我们会选择第一个和第三个子数组,因为它们的长度和 2 是最小值。

示例 3:

输入:arr = [4,3,2,6,2,3,4], target = 6
输出:-1
解释:我们只有一个和为 6 的子数组。

示例 4:

输入:arr = [5,5,4,4,5], target = 3
输出:-1
解释:我们无法找到和为 3 的子数组。

示例 5:

输入:arr = [3,1,1,1,5,1,2,1], target = 3
输出:3
解释:注意子数组 [1,2] 和 [2,1] 不能成为一个方案因为它们重叠了。

说明:

  • 1 <= arr.length <= 10^5
  • 1 <= arr[i] <= 1000
  • 1 <= target <= 10^8

思路

有一个整数数组 arr 和一个整数值 target,从数组中找到两个不重叠的子数组,使得子数组的和为 target,返回这两个子数组长度之和的最小值。

将重叠问题进行前后缀分解,在各自的部分只需考虑和为 target 的最小长度,最小长度可以使用滑动窗口。

代码


/**
 * @date 2026-09-17 10:06
 */
public class MinSumOfLengths1477 {

    public int minSumOfLengths(int[] arr, int target) {
        int n = arr.length;
        int[] pre = new int[n + 1];
        int[] suf = new int[n + 1];
        Arrays.fill(pre, Integer.MAX_VALUE);
        Arrays.fill(suf, Integer.MAX_VALUE);
        int sum = 0;
        int l = 0;
        for (int i = 0; i < n; i++) {
            sum += arr[i];
            while (sum > target) {
                sum -= arr[l++];
            }
            if (sum == target) {
                pre[i + 1] = Math.min(pre[i], i - l + 1);
            } else {
                pre[i + 1] = pre[i];
            }
        }
        sum = 0;
        int r = n - 1;
        for (int i = n - 1; i >= 0; i--) {
            sum += arr[i];
            while (sum > target) {
                sum -= arr[r--];
            }
            if (sum == target) {
                suf[i] = Math.min(suf[i + 1], r - i + 1);
            } else {
                suf[i] = suf[i + 1];
            }
        }
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            if (pre[i + 1] != Integer.MAX_VALUE && suf[i + 1] != Integer.MAX_VALUE) {
                res = Math.min(res, pre[i + 1] + suf[i + 1]);
            }
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }

}

性能

3483.不同三位偶数的数目

目标

给你一个数字数组 digits,你需要从中选择三个数字组成一个三位偶数,你的任务是求出 不同 三位偶数的数量。

注意:每个数字在三位偶数中都只能使用 一次 ,并且 不能 有前导零。

示例 1:

输入: digits = [1,2,3,4]
输出: 12
解释: 可以形成的 12 个不同的三位偶数是 124,132,134,142,214,234,312,314,324,342,412 和 432。注意,不能形成 222,因为数字 2 只有一个。

示例 2:

输入: digits = [0,2,2]
输出: 2
解释: 可以形成的三位偶数是 202 和 220。注意,数字 2 可以使用两次,因为数组中有两个 2 。

示例 3:

输入: digits = [6,6,6]
输出: 1
解释: 只能形成 666。

示例 4:

输入: digits = [1,3,5]
输出: 0
解释: 无法形成三位偶数。

说明:

  • 3 <= digits.length <= 10
  • 0 <= digits[i] <= 9

思路

有一个数字数组 digits,数组元素为 0 ~ 9 ,从中选择 3 个组成一个三位偶数,求不同的三位偶数的个数。

从结果来考虑,三位数 100 ~ 999,针对每个数字判断能否由数组中的数字构成即可。

从构造的角度考虑,需要保证个位是偶数,且百位不是 0,要求数字不同,需要维护可用的数字种类,需要知道每个数字的已使用次数,可以使用回溯。

代码


/**
 * @date 2026-09-11 8:56
 */
public class TotalNumbers3483 {

    public int totalNumbers(int[] digits) {
        int[] d = new int[10];
        for (int num : digits) {
            d[num]++;
        }
        int res = 0;
        for (int num = 100; num < 999; num += 2) {
            if (valid(num, d)) {
                res++;
            }
        }
        return res;
    }

    public boolean valid(int num, int[] d) {
        int[] tmp = new int[10];
        while (num > 0) {
            tmp[num % 10]++;
            num /= 10;
        }
        for (int i = 0; i < 10; i++) {
            if (d[i] < tmp[i]) {
                return false;
            }
        }
        return true;
    }

    public int totalNumbers_v0(int[] digits) {
        int[] d = new int[10];
        for (int num : digits) {
            d[num]++;
        }
        int res = 0;
        // 枚举百位
        for (int i = 1; i < d.length; i++) {
            if (d[i] == 0) {
                continue;
            }
            d[i]--;
            res += dfs(0, i, d);
            d[i]++;
        }

        return res;
    }

    int dfs(int index, int num, int[] d) {
        if (index == 2) {
            // 第三个数如果是偶数返回 1
            return num % 2 == 0 ? 1 : 0;
        }
        int res = 0;
        // 枚举十位
        for (int i = 0; i < d.length; i++) {
            if (d[i] == 0) {
                continue;
            }
            d[i]--;
            res += dfs(index + 1, i, d);
            d[i]++;
        }
        return res;
    }

}

性能

3090.每个字符最多出现两次的最长子字符串

目标

给你一个字符串 s ,请找出满足每个字符最多出现两次的最长子字符串,并返回该子字符串的 最大 长度。

示例 1:

输入: s = "bcbbbcba"
输出: 4
解释:
以下子字符串长度为 4,并且每个字符最多出现两次:"bcbbbcba"。

示例 2:

输入: s = "aaaa"
输出: 2
解释:
以下子字符串长度为 2,并且每个字符最多出现两次:"aaaa"。

说明:

  • 2 <= s.length <= 100
  • s 仅由小写英文字母组成。

思路

返回字符串 s 所有子串中每个字符出现次数不超过两次的最长子串长度。

滑动窗口。

代码


/**
 * @date 2026-08-14 8:44
 */
public class MaximumLengthSubstring3090 {

    public int maximumLengthSubstring(String s) {
        int n = s.length();
        int[] cnt = new int[26];
        int l = 0;
        int res = 0;
        for (int r = 0; r < n; r++) {
            int i = s.charAt(r) - 'a';
            cnt[i]++;
            while (cnt[i] > 2){
                cnt[s.charAt(l++) - 'a']--;
            }
            res = Math.max(res, r - l + 1);
        }
        return res;
    }
}

性能

2958.最多K个重复元素的最长子数组

目标

给你一个整数数组 nums 和一个整数 k 。

一个元素 x 在数组中的 频率 指的是它在数组中的出现次数。

如果一个数组中所有元素的频率都 小于等于 k ,那么我们称这个数组是 好 数组。

请你返回 nums 中 最长好 子数组的长度。

子数组 指的是一个数组中一段连续非空的元素序列。

示例 1:

输入:nums = [1,2,3,1,2,3,1,2], k = 2
输出:6
解释:最长好子数组是 [1,2,3,1,2,3] ,值 1 ,2 和 3 在子数组中的频率都没有超过 k = 2 。[2,3,1,2,3,1] 和 [3,1,2,3,1,2] 也是好子数组。
最长好子数组的长度为 6 。

示例 2:

输入:nums = [1,2,1,2,1,2,1,2], k = 1
输出:2
解释:最长好子数组是 [1,2] ,值 1 和 2 在子数组中的频率都没有超过 k = 1 。[2,1] 也是好子数组。
最长好子数组的长度为 2 。

示例 3:

输入:nums = [5,5,5,5,5,5,5], k = 4
输出:4
解释:最长好子数组是 [5,5,5,5] ,值 5 在子数组中的频率没有超过 k = 4 。
最长好子数组的长度为 4 。

说明:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^9
  • 1 <= k <= nums.length

思路

定义好子数组是元素频次不超过 k 的子数组,返回整数数组的最长好子数组长度。

滑动窗口。

代码


/**
 * @date 2026-08-12 9:19
 */
public class MaxSubarrayLength2958 {

    public int maxSubarrayLength(int[] nums, int k) {
        int n = nums.length;
        Map<Integer, Integer> cnt = new HashMap<>();
        int l = 0;
        int res = 0;
        for (int r = 0; r < n; r++) {
            cnt.merge(nums[r], 1, Integer::sum);
            while (cnt.get(nums[r]) > k) {
                cnt.merge(nums[l++], -1, Integer::sum);
            }
            res = Math.max(res, r - l + 1);
        }
        return res;
    }

}

性能

2996.大于等于顺序前缀和的最小缺失整数

目标

给你一个下标从 0 开始的整数数组 nums 。

如果一个前缀 nums[0..i] 满足对于 1 <= j <= i 的所有元素都有 nums[j] = nums[j - 1] + 1 ,那么我们称这个前缀是一个 顺序前缀 。特殊情况是,只包含 nums[0] 的前缀也是一个 顺序前缀 。

请你返回 nums 中没有出现过的 最小 整数 x ,满足 x 大于等于 最长 顺序前缀的和。

示例 1:

输入:nums = [1,2,3,2,5]
输出:6
解释:nums 的最长顺序前缀是 [1,2,3] ,和为 6 ,6 不在数组中,所以 6 是大于等于最长顺序前缀和的最小整数。

示例 2:

输入:nums = [3,4,5,1,12,14,13]
输出:15
解释:nums 的最长顺序前缀是 [3,4,5] ,和为 12 ,12、13 和 14 都在数组中,但 15 不在,所以 15 是大于等于最长顺序前缀和的最小整数。

说明:

  • 1 <= nums.length <= 50
  • 1 <= nums[i] <= 50

思路

定义数组 nums 的顺序前缀是满足 1 <= j <= i, nums[j] = nums[j - 1] + 1 的前缀,返回大于等于数组顺序前缀和且没有出现在数组的最小整数。

使用哈希表保存数组所有元素,计算顺序前缀和 sum,从 sum 返回第一个不在哈希表中的整数即可。

代码


/**
 * @date 2026-08-11 8:58
 */
public class MissingInteger2996 {

    public int missingInteger(int[] nums) {
        int n = nums.length;
        if (n == 1) {
            return nums[0] + 1;
        }
        Set<Integer> set = Arrays.stream(nums).boxed().collect(Collectors.toSet());
        int sum = nums[0];
        for (int i = 1; i < n; i++) {
            if (nums[i] != nums[i - 1] + 1) {
                break;
            }
            sum += nums[i];
        }
        while (set.contains(sum)) {
            sum++;
        }
        return sum;
    }
}

性能

3731.找出缺失的元素

目标

给你一个整数数组 nums ,数组由若干 互不相同 的整数组成。

数组 nums 原本包含了某个范围内的 所有整数 。但现在,其中可能 缺失 部分整数。

该范围内的 最小 整数和 最大 整数仍然存在于 nums 中。

返回一个 有序 列表,包含该范围内缺失的所有整数,并 按从小到大排序。如果没有缺失的整数,返回一个 空 列表。

示例 1:

输入: nums = [1,4,2,5]
输出: [3]
解释:
最小整数为 1,最大整数为 5,因此完整的范围应为 [1,2,3,4,5]。其中只有 3 缺失。

示例 2:

输入: nums = [7,8,6,9]
输出: []
解释:
最小整数为 6,最大整数为 9,因此完整的范围为 [6,7,8,9]。所有整数均已存在,因此没有缺失的整数。

示例 3:

输入: nums = [5,1]
输出: [2,3,4]
解释:
最小整数为 1,最大整数为 5,因此完整的范围应为 [1,2,3,4,5]。缺失的整数为 2、3 和 4。

说明:

  • 2 <= nums.length <= 100
  • 1 <= nums[i] <= 100

思路

有一个元素互不相同的数组,原本包含了 [min, max] 之间的所有整数,现在缺失了 (min, max) 中的一些元素,找到并返回缺失的元素。

首先找到数组的 minmax,将元素放入哈希表,遍历 (min, max) 判断数字是否在集合中。

代码


/**
 * @date 2026-08-04 8:49
 */
public class FindMissingElements3731 {

    public List<Integer> findMissingElements(int[] nums) {
        int min = 101, max = 0;
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            min = Math.min(min, num);
            max = Math.max(max, num);
            set.add(num);
        }
        List<Integer> res = new ArrayList<>();
        for (int i = min + 1; i < max; i++) {
            if (!set.contains(i)) {
                res.add(i);
            }
        }
        return res;
    }
}

性能

1331.数组序号转换

目标

给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。

序号代表了一个元素有多大。序号编号的规则如下:

  • 序号从 1 开始编号。
  • 一个元素越大,那么序号越大。如果两个元素相等,那么它们的序号相同。
  • 每个数字的序号都应该尽可能地小。

示例 1:

输入:arr = [40,10,20,30]
输出:[4,1,2,3]
解释:40 是最大的元素。 10 是最小的元素。 20 是第二小的数字。 30 是第三小的数字。

示例 2:

输入:arr = [100,100,100]
输出:[1,1,1]
解释:所有元素有相同的序号。

示例 3:

输入:arr = [37,12,28,9,100,56,80,5,12]
输出:[5,3,4,2,8,6,7,1,3]

说明:

  • 0 <= arr.length <= 10^5
  • -10^9 <= arr[i] <= 10^9

思路

有一个整数数组 arr,返回每个元素排序后的序号,序号从 1 开始,如果元素值相同那么序号也相同,并且后面的序号无需考虑前面重复的元素,比如 [10 20 20 30],返回 [1, 2, 2, 3] 而不是 [1, 2, 2, 4]

排序 arr 的下标数组 index,根据排序后的 index 返填原数组的序号,注意去重即可。

代码


/**
 * @date 2026-07-14 10:11
 */
public class ArrayRankTransform1331 {

    public int[] arrayRankTransform(int[] arr) {
        int n = arr.length;
        Integer[] index = new Integer[n];
        Arrays.setAll(index, i -> i);
        Arrays.sort(index, (a, b) -> (arr[a] - arr[b]));
        int prev = Integer.MIN_VALUE;
        int sort = 0;
        for (int i = 0; i < n; i++) {
            if (prev != arr[index[i]]){
                sort++;
            }
            prev = arr[index[i]];
            arr[index[i]] = sort;
        }
        return arr;
    }

}

性能

3020.子集中元素的最大数量

目标

给你一个 正整数 数组 nums 。

你需要从数组中选出一个满足下述条件的子集:

  • 你可以将选中的元素放置在一个下标从 0 开始的数组中,并使其遵循以下模式:[x, x^2, x^4, ..., x^k/2, x^k, x^k/2, ..., x^4, x^2, x](注意,k 可以是任何 非负 的 2 的幂)。例如,[2, 4, 16, 4, 2] 和 [3, 9, 3] 都符合这一模式,而 [2, 4, 8, 4, 2] 则不符合。

返回满足这些条件的子集中,元素数量的 最大值 。

示例 1:

输入:nums = [5,4,1,2,2]
输出:3
解释:选择子集 {4,2,2} ,将其放在数组 [2,4,2] 中,它遵循该模式,且 22 == 4 。因此答案是 3 。

示例 2:

输入:nums = [1,3,2,4]
输出:1
解释:选择子集 {1},将其放在数组 [1] 中,它遵循该模式。因此答案是 1 。注意我们也可以选择子集 {2} 、{4} 或 {3} ,可能存在多个子集都能得到相同的答案。

说明:

  • 2 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^9

思路

将正整数数组 nums 的子序列按照模式放入一个下标从 0 开始的数组中,模式为 [x x^2 x^4 x^8 ... x^k/2 x^k x^k/2 ... x^8 x^4 x^2 x]k 可以是任何非负的 2 的幂,即 1、2、4、8、16……。求满足条件的子序列的最大长度。

对数组中的元素计数,如果 x 出现次数大于等于 2 则开始倍增,将长度加 2,判断 x^2 是否出现,如果出现次数大于 1,则判断 (x^2)^2 是否出现,以此类推。循环结束时判断数字的出现次数,如果为 0 需要将前面的元素作为中间元素,之前长度加了 2 这里需要减 1,如果为 1 正好作为中间元素将长度加 1

代码


/**
 * @date 2026-06-29 17:33
 */
public class MaximumLength3020 {

    public int maximumLength(int[] nums) {
        Map<Integer, Integer> cnt = new HashMap<>();
        for (int num : nums) {
            cnt.merge(num, 1, Integer::sum);
        }
        int res = 1;
        if (cnt.get(1) != null) {
            res = Math.max(res, cnt.get(1) - (1 - cnt.get(1) % 2));
            cnt.remove(1);
        }
        for (Integer num : cnt.keySet()) {
            int l = 0;
            while (cnt.getOrDefault(num, 0) > 1) {
                l += 2;
                num *= num;
            }
            res = Math.max(res, l + (cnt.get(num) == null ? -1 : 1));
        }
        return res;
    }

}

性能

1189.”气球”的最大数量

目标

给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 "balloon"(气球)。

字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 "balloon"。

示例 1:

输入:text = "nlaebolko"
输出:1

示例 2:

输入:text = "loonbalxballpoon"
输出:2

示例 3:

输入:text = "leetcode"
输出:0

提示:

  • 1 <= text.length <= 10^4
  • text 全部由小写英文字母组成

注意:本题与 2287.重排字符形成目标字符串 相同。

思路

使用字符串 text 中的字母拼凑单词 balloon,求最多能拼出多少个。

单词由 1a1b1n2l2o 组成。对 text 中的字母计数,统计相关字母频次的最小值即可(lo 需要除以 2)。

代码


/**
 * @date 2026-06-22 9:05
 */
public class MaxNumberOfBalloons1189 {

    public int maxNumberOfBalloons_v1(String text) {
        char[] cnt = new char[26];
        for (char c : text.toCharArray()) {
            cnt[c - 'a']++;
        }
        int res = Integer.MAX_VALUE;
        res = Math.min(res, cnt[0]);
        res = Math.min(res, cnt[1]);
        res = Math.min(res, cnt[11] / 2);
        res = Math.min(res, cnt[13]);
        res = Math.min(res, cnt[14] / 2);
        return res;
    }

}

性能

2196.根据描述创建二叉树

目标

给你一个二维整数数组 descriptions ,其中 descriptions[i] = [parenti, childi, isLefti] 表示 parenti 是 childi 在 二叉树 中的 父节点,二叉树中各节点的值 互不相同 。此外:

  • 如果 isLefti == 1 ,那么 childi 就是 parenti 的左子节点。
  • 如果 isLefti == 0 ,那么 childi 就是 parenti 的右子节点。

请你根据 descriptions 的描述来构造二叉树并返回其 根节点 。

测试用例会保证可以构造出 有效 的二叉树。

示例 1:

输入:descriptions = [[20,15,1],[20,17,0],[50,20,1],[50,80,0],[80,19,1]]
输出:[50,20,80,15,17,19]
解释:根节点是值为 50 的节点,因为它没有父节点。
结果二叉树如上图所示。

示例 2:

输入:descriptions = [[1,2,1],[2,3,0],[3,4,1]]
输出:[1,2,null,null,3,4]
解释:根节点是值为 1 的节点,因为它没有父节点。 
结果二叉树如上图所示。 

说明:

  • 1 <= descriptions.length <= 10^4
  • descriptions[i].length == 3
  • 1 <= parenti, childi <= 10^5
  • 0 <= isLefti <= 1
  • descriptions 所描述的二叉树是一棵有效二叉树

思路

有一个二维数组 descriptionsdescriptions[i] = [parenti, childi, left or right] 描述了二叉树中的一对父子关系,即 parentichildi 的父节点,childiparenti 的左或者右孩子(取决于 1 或者 0)。重建二叉树并返回根节点。题目保证描述的是有效二叉树,且节点值不重复。

使用哈希表保存树节点,根据描述关系将节点连接起来,最终需要找出根节点,可以将孩子节点存到哈希集合中,返回不在该集合的节点即可。

代码


/**
 * @date 2026-06-08 11:34
 */
public class CreateBinaryTree2196 {

    public TreeNode createBinaryTree(int[][] descriptions) {
        Map<Integer, TreeNode> map = new HashMap<>();
        Set<Integer> childKeySet = new HashSet<>();
        for (int[] description : descriptions) {
            int parentKey = description[0];
            map.putIfAbsent(parentKey, new TreeNode(parentKey));
            TreeNode parent = map.get(parentKey);
            int childKey = description[1];
            childKeySet.add(childKey);
            map.putIfAbsent(childKey, new TreeNode(childKey));
            TreeNode child = map.get(childKey);
            if (description[2] == 1) {
                parent.left = child;
            } else {
                parent.right = child;
            }
        }
        for (int[] description : descriptions) {
            int parentKey = description[0];
            if (!childKeySet.contains(parentKey)) {
                return map.get(parentKey);
            }
        }
        return null;
    }
}

性能