LeetCode2441~2460题目解析

LeetCode 2441-2460. 题解合集

文章目录


2441. Largest Positive Integer That Exists With Its Negative(与对应负数同时存在的最大正整数)

题目描述

给你一个不包含任何零的整数数组 nums,找出自身与对应的负数都在数组中存在的最大正整数 k。

若不存在返回 -1。

示例 1

复制代码
`nums = [-1,2,-3,3]` → `3`。

示例 2

复制代码
`nums = [-1,10,6,7,-7,1]` → `7`。

示例 3

复制代码
`nums = [-10,8,6,7,-2,-3]` → `-1`。

提示:

  • 1 <= nums.length <= 1000,-1000 <= nums[i] <= 1000,nums[i] != 0

解题思路

解法一:哈希集合(主提交版本)

  • 把所有数放入 Set;遍历正数,若 -x 也在集合中则更新答案。
    解法二:标记数组(对照)
  • 值域只有 2001,用 boolean 数组标记正负出现,再扫描一遍。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(n) 或 O(2001)。

代码实现

java 复制代码
import java.util.HashSet;
import java.util.Set;

public class Solution {
    // 解法一:哈希集合(主提交版本)
    public int findMaxK(int[] nums) {
        Set<Integer> set = new HashSet<>();
        int ans = -1;
        for (int x : nums) {
            set.add(x);
        }
        for (int x : set) {
            if (x > 0 && set.contains(-x)) {
                ans = Math.max(ans, x);
            }
        }
        return ans;
    }

    // 解法二:标记数组(对照)
    public int findMaxK2(int[] nums) {
        boolean[] pos = new boolean[1001];
        boolean[] neg = new boolean[1001];
        for (int x : nums) {
            if (x > 0) {
                pos[x] = true;
            } else {
                neg[-x] = true;
            }
        }
        for (int k = 1000; k >= 1; k--) {
            if (pos[k] && neg[k]) {
                return k;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.findMaxK(new int[] {-1, 2, -3, 3}) == 3, "2441 ex1");
        check(sol.findMaxK2(new int[] {-1, 2, -3, 3}) == 3, "2441 ex12");
        check(sol.findMaxK(new int[] {-1, 10, 6, 7, -7, 1}) == 7, "2441 ex2");
        check(sol.findMaxK(new int[] {-10, 8, 6, 7, -2, -3}) == -1, "2441 ex3");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(109);
        for (int t = 0; t < 500; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = rnd.nextInt(40) - 20;
                if (a[i] == 0) {
                    a[i] = 1;
                }
            }
            int x = sol.findMaxK(a), y = sol.findMaxK2(a);
            if (x != y) {
                throw new AssertionError("2441 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2441 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n) 或 O(2001)。

2442. Count Number of Distinct Integers After Reverse Operations(反转之后不同整数的数目)

题目描述

给你一个由正 整数组成的数组 nums。必须取出数组中的每个整数,反转其中每个数位(去掉前导零),

并将反转后得到的数字添加到数组末尾。返回结果数组中不同整数的数目。

示例 1

复制代码
`nums = [1,13,10,12,31]` → `6`(反转后加入 1、31、1、21、13,去重后 {1,10,12,13,21,31})。

示例 2

复制代码
`nums = [2,2,2]` → `1`。

提示:

  • 1 <= nums.length <= 10^5,1 <= nums[i] <= 10^6

解题思路

解法一:哈希集合(主提交版本)

  • 每个数本身 + 反转后的数都放入 Set,返回 Set 大小。反转用除 10 取余法去掉前导零。
    解法二:字符串反转(对照)
  • 用 StringBuilder reverse 实现数字反转,思路不同的实现。
    复杂度:
  • 时间复杂度:O(n * L),L = 数字位数(≤7)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.HashSet;
import java.util.Set;

public class Solution {
    // 解法一:数学反转 + 哈希(主提交版本)
    public int countDistinctIntegers(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int x : nums) {
            set.add(x);
            int r = 0;
            while (x > 0) {
                r = r * 10 + x % 10;
                x /= 10;
            }
            set.add(r);
        }
        return set.size();
    }

    // 解法二:字符串反转(对照)
    public int countDistinctIntegers2(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int x : nums) {
            set.add(x);
            set.add(Integer.parseInt(new StringBuilder(String.valueOf(x)).reverse().toString()));
        }
        return set.size();
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.countDistinctIntegers(new int[] {1, 13, 10, 12, 31}) == 6, "2442 ex1");
        check(sol.countDistinctIntegers2(new int[] {1, 13, 10, 12, 31}) == 6, "2442 ex12");
        check(sol.countDistinctIntegers(new int[] {2, 2, 2}) == 1, "2442 ex2");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(113);
        for (int t = 0; t < 500; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(100000);
            }
            int x = sol.countDistinctIntegers(a), y = sol.countDistinctIntegers2(a);
            if (x != y) {
                throw new AssertionError("2442 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2442 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n * L),L = 数字位数(≤7)。
  • 空间复杂度:O(n)。

2443. Sum of Number and Its Reverse(反转之后的数字和)

题目描述

给你一个非负 整数 num。如果存在某个非负整数 k 满足 k + reverse(k) = num,返回 true,

否则返回 false(reverse(k) 是 k 反转每个数位后得到的数字,注意反转后可能含前导零)。

示例 1

复制代码
`num = 443` → `true`(172 + 271 = 443)。

示例 2

复制代码
`num = 63` → `false`。

示例 3

复制代码
`num = 181` → `true`(140 + 041 = 181)。

提示:

  • 0 <= num <= 10^5

解题思路

解法一:枚举 k(主提交版本)

  • 由于 k + reverse(k) = num、k 的位数不可能超过 num 的位数,枚举 k ∈ 0, num,
    检查等式是否成立即可(num ≤ 1e5,枚举一次 O(num*位数) 可过)。
    解法二:二分/数位构造(对照)
  • 无必要,直接也枚举但使用字符串反转的写法,两法对拍。
    复杂度:
  • 时间复杂度:O(num * log num)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {
    // 解法一:数学反转枚举(主提交版本)
    public boolean sumOfNumberAndReverse(int num) {
        for (int k = 0; k <= num; k++) {
            if (k + revNum(k) == num) {
                return true;
            }
        }
        return false;
    }

    private int revNum(int x) {
        int r = 0;
        while (x > 0) {
            r = r * 10 + x % 10;
            x /= 10;
        }
        return r;
    }

    // 解法二:字符串反转枚举(对照)
    public boolean sumOfNumberAndReverse2(int num) {
        for (int k = 0; k <= num; k++) {
            int r = Integer.parseInt(new StringBuilder(String.valueOf(k)).reverse().toString());
            if (k + r == num) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.sumOfNumberAndReverse(443) && sol.sumOfNumberAndReverse2(443), "2443 ex1");
        check(!sol.sumOfNumberAndReverse(63) && !sol.sumOfNumberAndReverse2(63), "2443 ex2");
        check(sol.sumOfNumberAndReverse(181) && sol.sumOfNumberAndReverse2(181), "2443 ex3");
        check(sol.sumOfNumberAndReverse(0), "2443 0(k=0)");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(127);
        for (int t = 0; t < 500; t++) {
            int n = rnd.nextInt(100001);
            boolean x = sol.sumOfNumberAndReverse(n), y = sol.sumOfNumberAndReverse2(n);
            if (x != y) {
                throw new AssertionError("2443 mismatch n=" + n + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2443 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(num * log num)。
  • 空间复杂度:O(1)。

2444. Count Subarrays With Fixed Bounds(统计定界子数组的数目)

题目描述

给你一个整数数组 nums 和两个整数 minK、maxK。nums 的定界子数组是满足:

  • 子数组中的最小值 等于 minK;
  • 子数组中的最大值 等于 maxK。
    返回定界子数组的数目。

示例 1

复制代码
`nums = [1,3,5,2,7,5], minK = 1, maxK = 5` → `2`。

示例 2

复制代码
`nums = [1,1,1,1], minK = 1, maxK = 1` → `10`。

提示:

  • 2 <= nums.length <= 10^5,1 <= nums[i], minK, maxK <= 10^6

解题思路

解法一:端点维护双指针(主提交版本)

  • 子数组不能包含任何 < minK 或 > maxK 的元素;用 bad 记录最近一次越界位置。
  • 记录最近一次出现 minK 的位置 lastMin 与 maxK 的位置 lastMax。
  • 以 i 为右端点,合法左端点必须在 bad+1 与 min(lastMin, lastMax) 之间,
    数量 = max(0, min(lastMin, lastMax) - bad),累加即答案。
    解法二:朴素枚举(对照,小规模)
  • 枚举左右端点并实时统计 min/max 校验,O(n^2)。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {
    // 解法一:端点维护(主提交版本)
    public long countSubarrays(int[] nums, int minK, int maxK) {
        long ans = 0;
        int bad = -1;
        int lastMin = -1, lastMax = -1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < minK || nums[i] > maxK) {
                bad = i;
                lastMin = lastMax = -1;
                continue;
            }
            if (nums[i] == minK) {
                lastMin = i;
            }
            if (nums[i] == maxK) {
                lastMax = i;
            }
            if (lastMin != -1 && lastMax != -1) {
                int leftBound = Math.min(lastMin, lastMax);
                ans += Math.max(0, leftBound - bad);
            }
        }
        return ans;
    }

    // 解法二:暴力枚举(对照)
    public long countSubarraysBrute(int[] nums, int minK, int maxK) {
        long ans = 0;
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            int mn = nums[i], mx = nums[i];
            for (int j = i; j < n; j++) {
                mn = Math.min(mn, nums[j]);
                mx = Math.max(mx, nums[j]);
                if (mn == minK && mx == maxK) {
                    ans++;
                }
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.countSubarrays(new int[] {1, 3, 5, 2, 7, 5}, 1, 5) == 2, "2444 ex1");
        check(sol.countSubarraysBrute(new int[] {1, 3, 5, 2, 7, 5}, 1, 5) == 2, "2444 ex1b");
        check(sol.countSubarrays(new int[] {1, 1, 1, 1}, 1, 1) == 10, "2444 ex2");
        check(sol.countSubarraysBrute(new int[] {1, 1, 1, 1}, 1, 1) == 10, "2444 ex2b");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(131);
        for (int t = 0; t < 500; t++) {
            int n = 2 + rnd.nextInt(40);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(6);
            }
            int mn = 1 + rnd.nextInt(5), mx = mn + rnd.nextInt(6 - mn);
            long x = sol.countSubarrays(a, mn, mx), y = sol.countSubarraysBrute(a, mn, mx);
            if (x != y) {
                throw new AssertionError("2444 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2444 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

2445. Number of Nodes With Value One(值为 1 的节点数)

题目描述

有一个无向树,n 个节点标记为 1...n,节点 v 的父节点是 floor(v/2)(根为 1)。最初每个节点值为 0。

对每个查询 queries[i],翻转节点 queries[i] 的子树内所有值。处理完所有查询后,返回值为 1 的节点总数。

示例 1

复制代码
`n = 5, queries = [1,2,5]` → `3`。

示例 2

复制代码
`n = 3, queries = [2,3,3]` → `1`。

提示:

  • 1 <= n <= 10^5,1 <= queries.length <= 10^5,1 <= queries[i] <= n

解题思路

解法一:次数奇偶性(主提交版本)

  • 子树翻转两次等于没翻,因此每个节点最终为 1 当且仅当「祖先链上被翻转次数」为奇数。
  • 根 1 的次数 = 查询中 1 的出现次数;节点 v 的次数 = 父节点次数 + 查询中 v 的出现次数。
    按编号从小到大遍历(父节点编号更小),累加统计奇数次节点数。
    解法二:暴力子树翻转模拟(对照,小规模)
  • 每次查询对子树所有节点异或 1,最后统计。
    复杂度:
  • 时间复杂度:O(n + q)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Solution {
    // 解法一:奇偶次数递推(主提交版本)
    public int numberOfNodes(int n, int[] queries) {
        int[] flips = new int[n + 1];
        for (int q : queries) {
            flips[q]++;
        }
        int[] eff = new int[n + 1];
        int ans = 0;
        for (int v = 1; v <= n; v++) {
            eff[v] = (eff[v / 2] + flips[v]) & 1;
            if (eff[v] == 1) {
                ans++;
            }
        }
        return ans;
    }

    // 解法二:暴力模拟(对照,小规模)
    public int numberOfNodesBrute(int n, int[] queries) {
        int[] val = new int[n + 1];
        for (int q : queries) {
            // 翻转 q 的子树:q, 2q, 2q+1, ...
            Set<Integer> sub = subtree(n, q);
            for (int v : sub) {
                val[v] ^= 1;
            }
        }
        int ans = 0;
        for (int v = 1; v <= n; v++) {
            ans += val[v];
        }
        return ans;
    }

    private Set<Integer> subtree(int n, int root) {
        Set<Integer> s = new HashSet<>();
        List<Integer> level = new ArrayList<>();
        level.add(root);
        while (!level.isEmpty()) {
            List<Integer> next = new ArrayList<>();
            for (int v : level) {
                s.add(v);
                int l = v * 2, r = v * 2 + 1;
                if (l <= n) {
                    next.add(l);
                }
                if (r <= n) {
                    next.add(r);
                }
            }
            level = next;
        }
        return s;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.numberOfNodes(5, new int[] {1, 2, 5}) == 3, "2445 ex1");
        check(sol.numberOfNodesBrute(5, new int[] {1, 2, 5}) == 3, "2445 ex1b");
        check(sol.numberOfNodes(3, new int[] {2, 3, 3}) == 1, "2445 ex2");
        check(sol.numberOfNodesBrute(3, new int[] {2, 3, 3}) == 1, "2445 ex2b");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(137);
        for (int t = 0; t < 300; t++) {
            int n = 1 + rnd.nextInt(60);
            int qn = 1 + rnd.nextInt(20);
            int[] qs = new int[qn];
            for (int i = 0; i < qn; i++) {
                qs[i] = 1 + rnd.nextInt(n);
            }
            int x = sol.numberOfNodes(n, qs), y = sol.numberOfNodesBrute(n, qs);
            if (x != y) {
                throw new AssertionError("2445 mismatch t=" + t + " n=" + n + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2445 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n + q)。
  • 空间复杂度:O(n)。

2446. Determine if Two Events Have Conflict(判断两个事件是否存在冲突)

题目描述

给你两个字符串数组 event1、event2,表示同一天的两个闭区间时间段,时间按 HH:MM 格式给出

(24 小时制)。两个事件存在某个非空的交集(某些时刻两事件都包含)时视为冲突,返回是否冲突。

示例 1

复制代码
`event1 = ["01:15","02:00"], event2 = ["02:00","03:00"]` → `true`。

示例 2

复制代码
`event1 = ["10:00","11:00"], event2 = ["14:00","15:00"]` → `false`。

提示:

  • 每个事件时间有效且 startTime <= endTime

解题思路

解法一:时间转分钟比较(主提交版本)

  • 将 "HH:MM" 解析为分钟数,两闭区间 s1,e1、s2,e2 相交当且仅当 s1 <= e2 && s2 <= e1。
    解法二:逐字符比较(对照)
  • 不转为分钟,直接按 HH:MM 字符串进行字典序比较(24 小时制下字典序与时间序一致)。
    复杂度:
  • 时间复杂度:O(1)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {
    // 解法一:转分钟比较(主提交版本)
    public boolean haveConflict(String[] event1, String[] event2) {
        int s1 = parse(event1[0]), e1 = parse(event1[1]);
        int s2 = parse(event2[0]), e2 = parse(event2[1]);
        return s1 <= e2 && s2 <= e1;
    }

    private int parse(String t) {
        return Integer.parseInt(t.substring(0, 2)) * 60 + Integer.parseInt(t.substring(3));
    }

    // 解法二:字符串比较(对照)
    public boolean haveConflict2(String[] event1, String[] event2) {
        return event1[0].compareTo(event2[1]) <= 0 && event2[0].compareTo(event1[1]) <= 0;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.haveConflict(new String[] {"01:15", "02:00"}, new String[] {"02:00", "03:00"}), "2446 ex1");
        check(sol.haveConflict2(new String[] {"01:15", "02:00"}, new String[] {"02:00", "03:00"}), "2446 ex12");
        check(!sol.haveConflict(new String[] {"10:00", "11:00"}, new String[] {"14:00", "15:00"}), "2446 ex2");
        check(!sol.haveConflict2(new String[] {"10:00", "11:00"}, new String[] {"14:00", "15:00"}), "2446 ex22");
        check(sol.haveConflict(new String[] {"01:00", "02:00"}, new String[] {"01:20", "03:00"}), "2446 ex3");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(139);
        for (int t = 0; t < 500; t++) {
            String[] e1 = randEvent(rnd), e2 = randEvent(rnd);
            boolean x = sol.haveConflict(e1, e2), y = sol.haveConflict2(e1, e2);
            if (x != y) {
                throw new AssertionError("2446 mismatch t=" + t);
            }
        }
        System.out.println("2446 全部 PASS");
    }

    private static String[] randEvent(java.util.Random rnd) {
        int h1 = rnd.nextInt(24), m1 = rnd.nextInt(60);
        int h2 = rnd.nextInt(24), m2 = rnd.nextInt(60);
        String t1 = String.format("%02d:%02d", h1, m1);
        String t2 = String.format("%02d:%02d", h2, m2);
        if (t1.compareTo(t2) > 0) {
            String tmp = t1;
            t1 = t2;
            t2 = tmp;
        }
        return new String[] {t1, t2};
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(1)。
  • 空间复杂度:O(1)。

2447. Number of Subarrays With GCD Equal to K(最大公因数等于 K 的子数组数目)

题目描述

给你一个整数数组 nums 和一个整数 k,统计 nums 的子数组中元素的最大公因数等于 k 的子数组数目。

示例 1

复制代码
`nums = [9,3,1,2,6,3], k = 3` → `4`。

示例 2

复制代码
`nums = [4], k = 7` → `0`。

提示:

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

解题思路

解法一:gcd 集合递推(主提交版本)

  • 遍历每个起点 i,维护「以 i 为起点的所有子数组 gcd 的集合」:gcd(x, v) 随长度增加而递减,
    集合大小 O(log A)。以每个起点新的 gcd = gcd(前一状态集合元素, numsi)。
  • 总复杂度 O(n log A)。
    解法二:暴力枚举(对照)
  • 对每个起点向后扩展 gcd,等于 k 时计数,O(n^2)。
    复杂度:
  • 时间复杂度:O(n log A)(主)或 O(n^2)(暴力)。
  • 空间复杂度:O(log A)。

代码实现

java 复制代码
public class Solution {
    // 解法一:gcd 集合递推(主提交版本)
    public int subarrayGCD(int[] nums, int k) {
        int ans = 0;
        java.util.Map<Integer, Integer> prev = new java.util.HashMap<>();
        for (int x : nums) {
            java.util.Map<Integer, Integer> cur = new java.util.HashMap<>();
            if (x % k == 0) {
                // 新的子数组起点 x
                cur.put(x, cur.getOrDefault(x, 0) + 1);
                for (java.util.Map.Entry<Integer, Integer> e : prev.entrySet()) {
                    int ng = gcd(e.getKey(), x);
                    cur.put(ng, cur.getOrDefault(ng, 0) + e.getValue());
                }
                ans += cur.getOrDefault(k, 0);
            }
            prev = cur;
        }
        return ans;
    }

    // 解法二:暴力(对照)
    public int subarrayGCD2(int[] nums, int k) {
        int ans = 0;
        for (int i = 0; i < nums.length; i++) {
            int g = 0;
            for (int j = i; j < nums.length; j++) {
                g = gcd(g, nums[j]);
                if (g == k) {
                    ans++;
                } else if (g < k) {
                    break; // gcd 只会更小,提前退出
                }
            }
        }
        return ans;
    }

    private static int gcd(int a, int b) {
        while (b != 0) {
            int t = a % b;
            a = b;
            b = t;
        }
        return a;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.subarrayGCD(new int[] {9, 3, 1, 2, 6, 3}, 3) == 4, "2447 ex1");
        check(sol.subarrayGCD2(new int[] {9, 3, 1, 2, 6, 3}, 3) == 4, "2447 ex12");
        check(sol.subarrayGCD(new int[] {4}, 7) == 0, "2447 ex2");
        check(sol.subarrayGCD2(new int[] {4}, 7) == 0, "2447 ex22");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(149);
        for (int t = 0; t < 500; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(100);
            }
            int k = 1 + rnd.nextInt(10);
            int x = sol.subarrayGCD(a, k), y = sol.subarrayGCD2(a, k);
            if (x != y) {
                throw new AssertionError("2447 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2447 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n log A)(主)或 O(n^2)(暴力)。
  • 空间复杂度:O(log A)。

2448. Minimum Cost to Make Array Equal(使数组相等的最小开销)

题目描述

给你两个下标从 0 开始的数组 nums 和 cost,分别包含 n 个正整数。可以执行任意次操作:

将 nums 中任意元素增加或减小 1。对第 i 个元素执行一次操作的开销是 cost[i]。

返回使 nums 中所有元素相等的最少总开销。

示例 1

复制代码
`nums=[1,3,5,2], cost=[2,3,1,14]` → `8`(全部变成 2)。

示例 2

复制代码
`nums=[2,2,2,2,2], cost=[4,2,8,1,3]` → `0`。

提示:

  • 1 <= n <= 10^5,1 <= nums[i], cost[i] <= 10^6,测试保证输出不超过 2^53-1

解题思路

解法一:带权中位数(主提交版本)

  • 目标值 x 的代价函数 f(x) = sum(cost[i] * |nums[i] - x|),是凸函数,
    最优 x 在「带权中位数」处:按 nums 排序后,找到前缀 cost 总和首次 ≥ 总 cost 一半的位置。
  • 这里直接枚举候选项取 min:排序后前缀扫描求从最小值出发的代价,再向右滑动更新 O(n log n)。
    解法二:三分/暴力枚举(对照)
  • 由于值域较大,采用对去重后的候选值暴力计算并比较(O(n^2))仅小规模对拍。
    复杂度:
  • 时间复杂度:O(n log n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.Arrays;

public class Solution {
    // 解法一:排序 + 带权中位数扫描(主提交版本)
    public long minCost(int[] nums, int[] cost) {
        int n = nums.length;
        Integer[] idx = new Integer[n];
        for (int i = 0; i < n; i++) {
            idx[i] = i;
        }
        Arrays.sort(idx, (a, b) -> Integer.compare(nums[a], nums[b]));
        long total = 0;
        for (long c : cost) {
            total += c;
        }
        // 找到带权中位数位置
        long half = (total + 1) / 2;
        long acc = 0;
        int pick = -1;
        for (int i = 0; i < n; i++) {
            acc += cost[idx[i]];
            if (acc >= half) {
                pick = nums[idx[i]];
                break;
            }
        }
        // 计算以 pick 为目标的总代价
        long ans = 0;
        for (int i = 0; i < n; i++) {
            ans += (long) Math.abs(nums[i] - pick) * cost[i];
        }
        return ans;
    }

    // 解法二:暴力枚举全部值为目标(对照,小规模)
    public long minCostBrute(int[] nums, int[] cost) {
        int lo = Integer.MAX_VALUE, hi = Integer.MIN_VALUE;
        for (int x : nums) {
            lo = Math.min(lo, x);
            hi = Math.max(hi, x);
        }
        long best = Long.MAX_VALUE;
        for (int p = lo; p <= hi; p++) {
            long cur = 0;
            for (int i = 0; i < nums.length; i++) {
                cur += (long) Math.abs(nums[i] - p) * cost[i];
            }
            best = Math.min(best, cur);
        }
        return best;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.minCost(new int[] {1, 3, 5, 2}, new int[] {2, 3, 1, 14}) == 8, "2448 ex1");
        check(sol.minCostBrute(new int[] {1, 3, 5, 2}, new int[] {2, 3, 1, 14}) == 8, "2448 ex1b");
        check(sol.minCost(new int[] {2, 2, 2, 2, 2}, new int[] {4, 2, 8, 1, 3}) == 0, "2448 ex2");
        check(sol.minCostBrute(new int[] {2, 2, 2, 2, 2}, new int[] {4, 2, 8, 1, 3}) == 0, "2448 ex2b");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(151);
        for (int t = 0; t < 300; t++) {
            int n = 1 + rnd.nextInt(12);
            int[] a = new int[n], c = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(20);
                c[i] = 1 + rnd.nextInt(10);
            }
            long x = sol.minCost(a, c), y = sol.minCostBrute(a, c);
            if (x != y) {
                throw new AssertionError("2448 mismatch t=" + t + " x=" + x + " y=" + y
                        + " a=" + Arrays.toString(a));
            }
        }
        System.out.println("2448 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n log n)。
  • 空间复杂度:O(n)。

2449. Minimum Number of Operations to Make Arrays Similar(使数组相似的最少操作次数)

题目描述

给你两个正整数数组 nums 和 target(等长)。一次操作:选两个不同下标 i、j,令

nums[i] += 2 且 nums[j] -= 2。两个数组每个元素出现频率相等则称相似。返回使 nums 与 target

相似的最少操作次数(测试保证有解)。

示例 1

复制代码
`nums=[8,12,6], target=[2,14,10]` → `2`。

示例 2

复制代码
`nums=[1,2,5], target=[4,1,3]` → `1`。

示例 3

复制代码
`nums=[1,1,1,1,1], target=[1,1,1,1,1]` → `0`。

提示:

  • 1 <= n <= 10^5,1 <= nums[i], target[i] <= 10^6

解题思路

解法一:奇偶分组贪心(主提交版本)

  • 每次操作 ±2 不改变元素奇偶性,因此 nums 与 target 中奇数和偶数必须分别对应。
  • 把 nums 与 target 各自按奇偶分成两组并排序;匹配时同一组内从小到大配对,
    需要的「上升总量」=sum(max(0, target2[i] - nums2[i]))(下降由别的元素承担)。
  • 一次操作同时给一个元素 +2、另一个 -2,因此操作数 = 上升总量 / 2。
    解法二:朴素双数组匹配(对照)
  • 显式模拟匹配过程:同样分组排序,统计正差和,除以 2。
    复杂度:
  • 时间复杂度:O(n log n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    // 解法一:奇偶分组贪心(主提交版本)
    public long makeSimilar(int[] nums, int[] target) {
        List<Integer> no = new ArrayList<>(), ne = new ArrayList<>();
        List<Integer> to = new ArrayList<>(), te = new ArrayList<>();
        for (int x : nums) {
            if ((x & 1) == 1) {
                no.add(x);
            } else {
                ne.add(x);
            }
        }
        for (int x : target) {
            if ((x & 1) == 1) {
                to.add(x);
            } else {
                te.add(x);
            }
        }
        no.sort(Integer::compareTo);
        ne.sort(Integer::compareTo);
        to.sort(Integer::compareTo);
        te.sort(Integer::compareTo);
        long inc = 0;
        for (int i = 0; i < no.size(); i++) {
            if (to.get(i) > no.get(i)) {
                inc += to.get(i) - no.get(i);
            }
        }
        for (int i = 0; i < ne.size(); i++) {
            if (te.get(i) > ne.get(i)) {
                inc += te.get(i) - ne.get(i);
            }
        }
        return inc / 2;
    }

    // 解法二:直接统计(对照,思路等价写法不同)
    public long makeSimilar2(int[] nums, int[] target) {
        Arrays.sort(nums);
        Arrays.sort(target);
        // 由于奇偶必须分开对应,先按奇偶稳定分组再求差
        int n = nums.length;
        int[] n2 = new int[n], t2 = new int[n];
        int po = 0, pe = 0, po2 = 0, pe2 = 0;
        for (int x : nums) {
            if ((x & 1) == 1) {
                n2[po++] = x;
            } else {
                n2[n - 1 - (pe++)] = x;
            }
        }
        for (int x : target) {
            if ((x & 1) == 1) {
                t2[po2++] = x;
            } else {
                t2[n - 1 - (pe2++)] = x;
            }
        }
        long inc = 0;
        for (int i = 0; i < n; i++) {
            if (t2[i] > n2[i]) {
                inc += t2[i] - n2[i];
            }
        }
        return inc / 2;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.makeSimilar(new int[] {8, 12, 6}, new int[] {2, 14, 10}) == 2, "2449 ex1");
        check(sol.makeSimilar2(new int[] {8, 12, 6}, new int[] {2, 14, 10}) == 2, "2449 ex12");
        check(sol.makeSimilar(new int[] {1, 2, 5}, new int[] {4, 1, 3}) == 1, "2449 ex2");
        check(sol.makeSimilar(new int[] {1, 1, 1, 1, 1}, new int[] {1, 1, 1, 1, 1}) == 0, "2449 ex3");
        // 随机对拍:从 target 反向生成合法 nums
        java.util.Random rnd = new java.util.Random(157);
        for (int t = 0; t < 300; t++) {
            int n = 2 + rnd.nextInt(10);
            int[] tg = new int[n];
            for (int i = 0; i < n; i++) {
                tg[i] = 1 + rnd.nextInt(30);
            }
            int[] ns = tg.clone();
            for (int i = 0; i < n; i++) {
                ns[i] += (rnd.nextBoolean() ? 2 : -2) * (1 + rnd.nextInt(5));
                if (ns[i] < 1) {
                    ns[i] = tg[i]; // 保证合法(奇偶一致)
                }
            }
            // 确保奇偶一致:修正
            for (int i = 0; i < n; i++) {
                if ((ns[i] & 1) != (tg[i] & 1)) {
                    ns[i] += 1;
                }
            }
            long x = sol.makeSimilar(ns, tg), y = sol.makeSimilar2(ns, tg);
            if (x != y) {
                throw new AssertionError("2449 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2449 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n log n)。
  • 空间复杂度:O(n)。

2450. Number of Distinct Binary Strings After Applying Operations(应用操作后不同二进制字符串的数量)

题目描述

给定一个_二进制_字符串 s 和一个正整数 k。可以应用操作任意次:从 s 中选择任何大小为 k 的

子字符串,将其所有字符翻转。返回可以获得的不同 字符串的数量,对 10^9+7 取模。

示例 1

复制代码
`s = "1001", k = 3` → `4`。

示例 2

复制代码
`s = "10110", k = 5` → `2`。

提示:

  • 1 <= k <= s.length <= 10^5,s[i] 是 '0' 或 '1'

解题思路

解法一:线性基/矩阵秩(主提交版本,数学推导)

  • 翻转固定长度 k 的连续段,可视为在 GF(2) 上对布尔向量做线性变换。所有可达字符串构成的
    仿射空间大小 = 2^rank。已知结论(官方题解):若 k 与 n 满足一定条件,秩 = n 或 n-1 或特殊。
    简单实现:用「翻转差分数组」思路,只考虑长度为 k 的段的操作组合,差分变换的秩:
    对每个位置 i,定义翻转该段为对差分数组的两位取反;互相独立的翻转组合数通过并查集/线性基计算。
  • 这里采用通用可通行解法:把长度 k 的翻转段看成 n-k+1 个线性方程作用在 n 位上,
    用并查集/线性基计算可达空间维数,答案 = 2^rank % MOD。注意 n ≤ 1e5 时直接线性基 O(n*bits) 可行。
    实际更简单:翻转段操作等价于在 GF(2) 上生成空间,其秩 = n - (k 与 n 的某种关系);
    标准结论:rank = n(若 k 为奇数)或 n-1(若 k 为偶数且 n-k 为偶数时)等。为稳妥直接算。
    解法二:小规模暴力 BFS(对照)
  • 小规模时枚举所有可达状态(位掩码翻转),返回集合大小,用于对拍。
    复杂度:
  • 时间复杂度:O(n * bits) 或 O(n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.HashSet;
import java.util.Set;

public class Solution {
    private static final int MOD = 1000000007;

    // 解法一:线性基求秩(主提交版本)
    public int countDistinctStrings(String s, int k) {
        int n = s.length();
        int m = n - k + 1; // 翻转段数
        int[] basis = new int[k]; // 线性基,向量长度 k(n 位由 m 个段向量的线性组合决定)
        // 把每个候选「整体翻转」视为 m 个向量用 GF(2) 线性基求秩的几何解释:
        // 更稳定做法:直接计算可达状态的维度------已知二阶差分技巧:
        // 定义 diff[i] = s[i] ^ s[i-1],翻转 [l, l+k-1] 等价于 diff[l] 与 diff[l+k] 变号。
        // diff 数组长度 n+1,可翻转的独立二元组 (l, l+k) 形成若干链;每条链长度 L 的自由度 = L-1
        // (首尾可被固定),但两端 diff[0]、diff[n] 不合法(越界)时调整。官方解法秩计算复杂。
        // 回到实现:用并查集把位置 i 与 i+k 连边(表示二者构成的可翻转对),
        // 连通块的自由度 = 块大小(奇数环的部分)------由于我们处理的是差分数组的「翻转两位」,
        // 实际上每次操作翻转两个 diff 位。
        // 定义数组 d[0..n],翻转 [l, l+k-1] 使 d[l] 与 d[l+k] 异或 1。
        // 可操作的边 = 所有 (l, l+k),0<=l<=n-k。把这些对(下标 l 与 l+k)用并查集连接,
        // 每个连通分量大小为 sz:分量内每对边缘相互影响,等价于向量数 = sz-1(任取一点为基准)。
        // rank = sum(sz-1) 对所有连通分量 = n+1 - 连通分量个数。
        int N = n + 1;
        int[] parent = new int[N];
        for (int i = 0; i < N; i++) {
            parent[i] = i;
        }
        for (int l = 0; l + k <= n; l++) {
            union(parent, l, l + k);
        }
        Set<Integer> roots = new HashSet<>();
        for (int i = 0; i < N; i++) {
            roots.add(find(parent, i));
        }
        int rank = N - roots.size(); // n+1 - comps
        // 但差分数组首位 d[0] 与末位 d[n] 是固定(虚拟)位吗?实际上 d[0] 对应 s[0](xor 0),
        // d[n] 对应翻转后的 s[n-1]、无实际约束------原串信息只有 n 位。最终可达字符串数 = 2^有效秩,
        // 有效秩需仔细对齐:达空间维数 = rank(上面算的)------验证对 k=n:l=0 唯一对 (0,n),
        // comps = n,rank = n+1-n = 1?但 k=n 时只有整体翻转,可达 2 个字符串 → 秩应为 1 ✅。
        return (int) pow2(rank);
    }

    private void union(int[] p, int a, int b) {
        int ra = find(p, a), rb = find(p, b);
        if (ra != rb) {
            p[ra] = rb;
        }
    }

    private int find(int[] p, int x) {
        while (p[x] != x) {
            p[x] = p[p[x]];
            x = p[x];
        }
        return x;
    }

    private long pow2(int e) {
        long r = 1, a = 2;
        while (e > 0) {
            if ((e & 1) == 1) {
                r = r * a % MOD;
            }
            a = a * a % MOD;
            e >>= 1;
        }
        return r;
    }

    // 解法二:暴力 BFS(对照,小规模)
    public int countDistinctStringsBrute(String s, int k) {
        Set<String> seen = new HashSet<>();
        java.util.ArrayDeque<String> q = new java.util.ArrayDeque<>();
        seen.add(s);
        q.add(s);
        int n = s.length();
        while (!q.isEmpty()) {
            String cur = q.poll();
            for (int l = 0; l + k <= n; l++) {
                StringBuilder sb = new StringBuilder(cur);
                for (int i = l; i < l + k; i++) {
                    sb.setCharAt(i, sb.charAt(i) == '0' ? '1' : '0');
                }
                String ns = sb.toString();
                if (seen.add(ns)) {
                    q.add(ns);
                }
            }
        }
        return seen.size();
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.countDistinctStrings("1001", 3) == 4, "2450 ex1");
        check(sol.countDistinctStringsBrute("1001", 3) == 4, "2450 ex1b");
        check(sol.countDistinctStrings("10110", 5) == 2, "2450 ex2");
        check(sol.countDistinctStringsBrute("10110", 5) == 2, "2450 ex2b");
        // 随机对拍(小规模)
        java.util.Random rnd = new java.util.Random(163);
        for (int t = 0; t < 200; t++) {
            int n = 1 + rnd.nextInt(9);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < n; i++) {
                sb.append(rnd.nextBoolean() ? '1' : '0');
            }
            int k = 1 + rnd.nextInt(n);
            int x = sol.countDistinctStrings(sb.toString(), k);
            int y = sol.countDistinctStringsBrute(sb.toString(), k);
            if (x != y) {
                throw new AssertionError("2450 mismatch s=" + sb + " k=" + k + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2450 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n * bits) 或 O(n)。
  • 空间复杂度:O(n)。

2451. Odd String Difference(差值数组不同的字符串)

题目描述

给你一个字符串数组 words(长度都相同 n)。每个字符串可转为长度 n-1 的差值整数数组

difference[i][j] = words[i][j+1] - words[i][j](按字母位置差值)。除了一个字符串外其余差值数组

都相同。返回差值数组不同的那个字符串。

示例 1

复制代码
`words = ["adc","wzy","abc"]` → `"abc"`。

示例 2

复制代码
`words = ["aaa","bob","ccc","ddd"]` → `"bob"`。

提示:

  • 3 <= words.length <= 100,2 <= n <= 20,只含小写字母

解题思路

解法一:哈希统计差值模式(主提交版本)

  • 把每个字符串的差值数组编码为 String(用逗号分隔),统计出现次数,返回出现 1 次的字符串。
    解法二:直接找不同(对照)
  • 取前两个的差值,若相等则找与它不同的;否则比较哪个与第三个相同。
    复杂度:
  • 时间复杂度:O(n * L)。
  • 空间复杂度:O(n)。(n 为 words 数量)

代码实现

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class Solution {
    // 解法一:哈希统计(主提交版本)
    public String oddString(String[] words) {
        Map<String, Integer> cnt = new HashMap<>();
        Map<String, String> pos = new HashMap<>();
        for (String w : words) {
            String key = encode(w);
            cnt.put(key, cnt.getOrDefault(key, 0) + 1);
            pos.put(key, w);
        }
        for (Map.Entry<String, Integer> e : cnt.entrySet()) {
            if (e.getValue() == 1) {
                return pos.get(e.getKey());
            }
        }
        return "";
    }

    private String encode(String w) {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < w.length(); i++) {
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append(w.charAt(i) - w.charAt(i - 1));
        }
        return sb.toString();
    }

    // 解法二:先测前两个(对照)
    public String oddString2(String[] words) {
        String d0 = diff(words[0]), d1 = diff(words[1]), d2 = diff(words[2]);
        String other = d0.equals(d1) ? d0 : (d0.equals(d2) ? d0 : d1);
        for (String w : words) {
            if (!diff(w).equals(other)) {
                return w;
            }
        }
        return "";
    }

    private String diff(String w) {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < w.length(); i++) {
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append(w.charAt(i) - w.charAt(i - 1));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.oddString(new String[] {"adc", "wzy", "abc"}).equals("abc"), "2451 ex1");
        check(sol.oddString2(new String[] {"adc", "wzy", "abc"}).equals("abc"), "2451 ex12");
        check(sol.oddString(new String[] {"aaa", "bob", "ccc", "ddd"}).equals("bob"), "2451 ex2");
        check(sol.oddString2(new String[] {"aaa", "bob", "ccc", "ddd"}).equals("bob"), "2451 ex22");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(167);
        for (int t = 0; t < 300; t++) {
            int n = 3 + rnd.nextInt(6);
            int len = 2 + rnd.nextInt(6);
            // 构造 n-1 个相同 + 1 个不同的字符串
            StringBuilder base = new StringBuilder();
            for (int i = 0; i < len; i++) {
                base.append((char) ('a' + rnd.nextInt(5)));
            }
            String[] ws = new String[n];
            int oddPos = rnd.nextInt(n);
            String odd = "";
            // 保证 odd 的差值数组与 base 不同
            do {
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < len; j++) {
                    sb.append((char) ('a' + rnd.nextInt(5)));
                }
                odd = sb.toString();
            } while (diffOf(odd).equals(diffOf(base.toString())));
            for (int i = 0; i < n; i++) {
                ws[i] = (i == oddPos) ? odd : base.toString();
            }
            String x = sol.oddString(ws), y = sol.oddString2(ws);
            if (!x.equals(y) || !x.equals(odd)) {
                throw new AssertionError("2451 mismatch t=" + t + " x=" + x + " y=" + y + " odd=" + odd);
            }
        }
        System.out.println("2451 全部 PASS");
    }

    private static String diffOf(String w) {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < w.length(); i++) {
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append(w.charAt(i) - w.charAt(i - 1));
        }
        return sb.toString();
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n * L)。
  • 空间复杂度:O(n)。(n 为 words 数量)

2452. Words Within Two Edits of Dictionary(距离字典两次编辑以内的单词)

题目描述

给你两个字符串数组 queries 和 dictionary(所有单词长度相同,只含小写字母)。

一次编辑:把任意一个字母修改成任何其他字母。返回 queries 中满足「不超过两次编辑内与 dictionary

中某个字符串相同」的单词,顺序与输入一致。

示例 1

复制代码
`queries=["word","note","ants","wood"], dictionary=["wood","joke","moat"]` → `["word","note","wood"]`。

示例 2

复制代码
`queries=["yes"], dictionary=["not"]` → `[]`。

提示:

  • 1 <= queries.length, dictionary.length <= 100,1 <= n <= 100

解题思路

解法一:按位差异计数(主提交版本)

  • 对 queries 的每个 w,遍历 dictionary 每个 d 统计对应位置不同数 ≤ 2 即可。
    O(Q * D * L) = 100100 100 = 1e6,直接过。
    解法二:剪枝 + 提前退出(对照)
  • 相同结构但用 char 数组先拷贝、位差异计数,写法不同。
    复杂度:
  • 时间复杂度:O(Q * D * L)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Solution {
    // 解法一:直接计数(主提交版本)
    public List<String> twoEditWords(String[] queries, String[] dictionary) {
        List<String> ans = new ArrayList<>();
        for (String q : queries) {
            for (String d : dictionary) {
                int diff = 0;
                for (int i = 0; i < q.length(); i++) {
                    if (q.charAt(i) != d.charAt(i)) {
                        diff++;
                        if (diff > 2) {
                            break;
                        }
                    }
                }
                if (diff <= 2) {
                    ans.add(q);
                    break;
                }
            }
        }
        return ans;
    }

    // 解法二:优化写法(对照)
    public List<String> twoEditWords2(String[] queries, String[] dictionary) {
        List<String> ans = new ArrayList<>();
        char[][] dict = new char[dictionary.length][];
        for (int i = 0; i < dictionary.length; i++) {
            dict[i] = dictionary[i].toCharArray();
        }
        for (String q : queries) {
            char[] qc = q.toCharArray();
            boolean ok = false;
            for (char[] d : dict) {
                int diff = 0;
                for (int i = 0; i < qc.length; i++) {
                    if (qc[i] != d[i]) {
                        diff++;
                    }
                }
                if (diff <= 2) {
                    ok = true;
                    break;
                }
            }
            if (ok) {
                ans.add(q);
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        List<String> r1 = sol.twoEditWords(new String[] {"word", "note", "ants", "wood"},
                new String[] {"wood", "joke", "moat"});
        check(r1.toString().equals("[word, note, wood]"), "2452 ex1 " + r1);
        check(sol.twoEditWords2(new String[] {"word", "note", "ants", "wood"},
                new String[] {"wood", "joke", "moat"}).size() == 3, "2452 ex12");
        check(sol.twoEditWords(new String[] {"yes"}, new String[] {"not"}).isEmpty(), "2452 ex2");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(173);
        for (int t = 0; t < 300; t++) {
            int qn = 1 + rnd.nextInt(8), dn = 1 + rnd.nextInt(8);
            int len = 1 + rnd.nextInt(6);
            String[] qs = new String[qn], ds = new String[dn];
            for (int i = 0; i < qn; i++) {
                qs[i] = randStr(rnd, len);
            }
            for (int i = 0; i < dn; i++) {
                ds[i] = randStr(rnd, len);
            }
            List<String> x = sol.twoEditWords(qs, ds), y = sol.twoEditWords2(qs, ds);
            if (!x.equals(y)) {
                throw new AssertionError("2452 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2452 全部 PASS");
    }

    private static String randStr(java.util.Random rnd, int len) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            sb.append((char) ('a' + rnd.nextInt(6)));
        }
        return sb.toString();
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(Q * D * L)。
  • 空间复杂度:O(1)。

2453. Destroy Sequential Targets(摧毁一系列目标)

题目描述

给你一个下标从 0 开始的数组 nums(正整数,数轴上目标的位置)和一个整数 space。

给机器输入 nums[i],它会摧毁所有位置在 nums[i] + c * space(c 为非负整数)的目标。

想要摧毁尽可能多的目标,返回在摧毁数目最多前提下 nums[i] 的最小值。

示例 1

复制代码
`nums=[3,7,8,1,1,5], space=2` → `1`。

示例 2

复制代码
`nums=[1,3,5,2,4,6], space=2` → `1`。

示例 3

复制代码
`nums=[6,2,5], space=100` → `2`。

提示:

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

解题思路

解法一:同余分组(主提交版本)

  • 输入参数 x 摧毁的目标是与其模 space 同余的所有 numsi(因为 x + c*space)。
  • 按 nums[i] % space 分组计数;取计数最大的组中最小元素。
    解法二:TreeMap 统计(对照)
  • 用有序结构记录每组最小元素,逻辑相同、实现不同。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class Solution {
    // 解法一:同余分组(主提交版本)
    public int destroyTargets(int[] nums, int space) {
        Map<Integer, Integer> cnt = new HashMap<>();
        Map<Integer, Integer> minOf = new HashMap<>();
        for (int x : nums) {
            int key = ((x % space) + space) % space;
            cnt.put(key, cnt.getOrDefault(key, 0) + 1);
            minOf.put(key, Math.min(minOf.getOrDefault(key, Integer.MAX_VALUE), x));
        }
        int bestCnt = -1, bestVal = Integer.MAX_VALUE;
        for (Map.Entry<Integer, Integer> e : cnt.entrySet()) {
            if (e.getValue() > bestCnt || (e.getValue() == bestCnt && minOf.get(e.getKey()) < bestVal)) {
                bestCnt = e.getValue();
                bestVal = minOf.get(e.getKey());
            }
        }
        return bestVal;
    }

    // 解法二:统计后扫描(对照)
    public int destroyTargets2(int[] nums, int space) {
        Map<Integer, Integer> cnt = new HashMap<>();
        for (int x : nums) {
            int key = ((x % space) + space) % space;
            cnt.put(key, cnt.getOrDefault(key, 0) + 1);
        }
        int bestCnt = -1, bestVal = Integer.MAX_VALUE;
        for (int x : nums) {
            int key = ((x % space) + space) % space;
            int c = cnt.get(key);
            if (c > bestCnt || (c == bestCnt && x < bestVal)) {
                bestCnt = c;
                bestVal = x;
            }
        }
        return bestVal;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.destroyTargets(new int[] {3, 7, 8, 1, 1, 5}, 2) == 1, "2453 ex1");
        check(sol.destroyTargets2(new int[] {3, 7, 8, 1, 1, 5}, 2) == 1, "2453 ex12");
        check(sol.destroyTargets(new int[] {1, 3, 5, 2, 4, 6}, 2) == 1, "2453 ex2");
        check(sol.destroyTargets(new int[] {6, 2, 5}, 100) == 2, "2453 ex3");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(179);
        for (int t = 0; t < 500; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(100);
            }
            int sp = 1 + rnd.nextInt(20);
            int x = sol.destroyTargets(a, sp), y = sol.destroyTargets2(a, sp);
            if (x != y) {
                throw new AssertionError("2453 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2453 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

2454. Next Greater Element IV(下一个更大元素 IV)

题目描述

给你一个下标从 0 开始的非负整数数组 nums。nums[j] 称为 nums[i] 的第二大整数,若:

  1. j > i;2) nums[j] > nums[i];3) 恰好存在一个 k 满足 i < k < j 且 nums[k] > nums[i]。
    返回 answer,answer[i] 是第二大整数,不存在则为 -1。

示例 1

复制代码
`nums = [2,4,0,9,6]` → `[9,6,6,-1,-1]`。

示例 2

复制代码
`nums = [3,3]` → `[-1,-1]`。

提示:

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

解题思路

解法一:单调栈 + 小根堆(主提交版本)

  • 从左到右扫描,用单调递减栈维护「只被弹出的元素」:当 nums[i] 大于栈顶时,栈顶元素
    找到了第一个更大元素(就是 numsi),把它们移入一个小根堆(存「已经找到一个更大元素的候选下标」,
    堆按 nums 值排序)。
  • 每次遇到新元素 numsi,先处理堆:凡堆顶值 < numsi 的,说明 numsi 是它的第二个更大元素
    (第一个已被它们弹出它们时遇到的那个元素找到------准确说:堆顶元素在之前已被某个更大元素弹出,
    现在遇到更大的 numsi 即是第二个更大),记录答案并弹出。
  • 然后处理单调栈(负责找「第一个更大元素」)。
    解法二:暴力枚举(对照,小规模)
  • 对每个 i 向右找第一个更大、再找第二个更大。
    复杂度:
  • 时间复杂度:O(n log n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.PriorityQueue;

public class Solution {
    // 解法一:单调栈 + 小根堆(主提交版本)
    public int[] secondGreaterElement(int[] nums) {
        int n = nums.length;
        int[] ans = new int[n];
        Arrays.fill(ans, -1);
        Deque<Integer> stack = new ArrayDeque<>(); // 单调递减栈,存下标
        PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> Integer.compare(nums[a], nums[b]));
        for (int i = 0; i < n; i++) {
            // 先处理已经找到第一个更大元素的候选:nums[i] 是它们的第二个更大元素
            while (!pq.isEmpty() && nums[pq.peek()] < nums[i]) {
                ans[pq.poll()] = nums[i];
            }
            // 找第一个更大元素:弹出所有小于 nums[i] 的
            while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
                pq.offer(stack.pop()); // 找到了第一个更大元素(nums[i])
            }
            stack.push(i);
        }
        return ans;
    }

    // 解法二:暴力(对照)
    public int[] secondGreaterElementBrute(int[] nums) {
        int n = nums.length;
        int[] ans = new int[n];
        Arrays.fill(ans, -1);
        for (int i = 0; i < n; i++) {
            int first = -1, second = -1;
            for (int j = i + 1; j < n; j++) {
                if (nums[j] > nums[i]) {
                    if (first == -1) {
                        first = j;
                    } else {
                        second = j;
                        break;
                    }
                }
            }
            if (second != -1) {
                ans[i] = nums[second];
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(Arrays.toString(sol.secondGreaterElement(new int[] {2, 4, 0, 9, 6})).equals("[9, 6, 6, -1, -1]"),
                "2454 ex1");
        check(Arrays.toString(sol.secondGreaterElement(new int[] {3, 3})).equals("[-1, -1]"), "2454 ex2");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(181);
        for (int t = 0; t < 500; t++) {
            int n = 1 + rnd.nextInt(12);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = rnd.nextInt(30);
            }
            int[] x = sol.secondGreaterElement(a), y = sol.secondGreaterElementBrute(a);
            if (!Arrays.equals(x, y)) {
                throw new AssertionError("2454 mismatch t=" + t + " x=" + Arrays.toString(x)
                        + " y=" + Arrays.toString(y) + " a=" + Arrays.toString(a));
            }
        }
        System.out.println("2454 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n log n)。
  • 空间复杂度:O(n)。

2455. Average Value of Even Numbers That Are Divisible by Three(可被三整除的偶数的平均值)

题目描述

给你一个由正整数组成的整数数组 nums,返回其中可被 3 整除的所有偶数的平均值(向下取整)。

若不存在符合条件的数返回 0。

示例 1

复制代码
`nums = [1,3,6,10,12,15]` → `9`(6 和 12,(6+12)/2=9)。

示例 2

复制代码
`nums = [1,2,4,7,10]` → `0`。

提示:

  • 1 <= nums.length <= 1000,1 <= nums[i] <= 1000

解题思路

解法一:单遍筛选(主提交版本)

  • 可被 3 整除的偶数即能被 6 整除的数。累加并计数,返回 sum/count(整除即向下取整)。
    解法二:显式条件(对照)
  • 用 x % 2 == 0 && x % 3 == 0 的写法,语义等价。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {
    // 解法一:能被 6 整除(主提交版本)
    public int averageValue(int[] nums) {
        int sum = 0, cnt = 0;
        for (int x : nums) {
            if (x % 6 == 0) {
                sum += x;
                cnt++;
            }
        }
        return cnt == 0 ? 0 : sum / cnt;
    }

    // 解法二:显式条件(对照)
    public int averageValue2(int[] nums) {
        int sum = 0, cnt = 0;
        for (int x : nums) {
            if (x % 2 == 0 && x % 3 == 0) {
                sum += x;
                cnt++;
            }
        }
        return cnt == 0 ? 0 : sum / cnt;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.averageValue(new int[] {1, 3, 6, 10, 12, 15}) == 9, "2455 ex1");
        check(sol.averageValue2(new int[] {1, 3, 6, 10, 12, 15}) == 9, "2455 ex12");
        check(sol.averageValue(new int[] {1, 2, 4, 7, 10}) == 0, "2455 ex2");
        check(sol.averageValue2(new int[] {1, 2, 4, 7, 10}) == 0, "2455 ex22");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(191);
        for (int t = 0; t < 500; t++) {
            int n = 1 + rnd.nextInt(30);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = 1 + rnd.nextInt(1000);
            }
            int x = sol.averageValue(a), y = sol.averageValue2(a);
            if (x != y) {
                throw new AssertionError("2455 mismatch t=" + t + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2455 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

题目描述

给你字符串数组 creators、ids 和整数数组 views(长度均为 n)。第 i 个视频的创作者为

creators[i]、id 为 ids[i]、播放量为 views[i]。创作者的流行度 = 其所有视频播放量总和。

找出流行度最高 的创作者(多个都返回)及其播放量最高的视频 id;若某创作者存在多个播放量最高

的视频,返回字典序最小的 id。返回二维字符串数组。

示例 1

复制代码
`creators=["alice","bob","alice","chris"], ids=["one","two","three","four"], views=[5,10,5,4]`
→ `[["alice","one"],["bob","two"]]`。

示例 2

复制代码
`creators=["alice","alice","alice"], ids=["a","b","c"], views=[1,2,2]` → `[["alice","b"]]`。

提示:

  • 1 <= n <= 10^5,创作者/视频 id 长度 1~5,小写字母

解题思路

解法一:哈希聚合(主提交版本)

  • 遍历一次:每创作者维护 总播放量、最佳视频(播放量, id 字典序)(用 maxViews 与 bestId)。
  • 扫描全部创作者取最大流行度,收集流行度等于最大值的创作者。
    解法二:Map + 排序(对照)
  • 聚合后对创作者排序,再输出,思路不同。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {
    private static class Info {
        long total;
        int maxView = -1;
        String bestId = "";
    }

    // 解法一:哈希聚合(主提交版本)
    public List<List<String>> mostPopularCreator(String[] creators, String[] ids, int[] views) {
        Map<String, Info> map = new HashMap<>();
        for (int i = 0; i < creators.length; i++) {
            Info inf = map.computeIfAbsent(creators[i], k -> new Info());
            inf.total += views[i];
            if (views[i] > inf.maxView || (views[i] == inf.maxView && ids[i].compareTo(inf.bestId) < 0)) {
                inf.maxView = views[i];
                inf.bestId = ids[i];
            }
        }
        long maxTotal = 0;
        for (Info inf : map.values()) {
            maxTotal = Math.max(maxTotal, inf.total);
        }
        List<List<String>> ans = new ArrayList<>();
        for (Map.Entry<String, Info> e : map.entrySet()) {
            if (e.getValue().total == maxTotal) {
                List<String> row = new ArrayList<>();
                row.add(e.getKey());
                row.add(e.getValue().bestId);
                ans.add(row);
            }
        }
        return ans;
    }

    // 解法二:双 Map 聚合 + 排序(对照)
    public List<List<String>> mostPopularCreator2(String[] creators, String[] ids, int[] views) {
        Map<String, Long> total = new HashMap<>();
        Map<String, String> best = new HashMap<>();
        Map<String, Integer> maxV = new HashMap<>();
        for (int i = 0; i < creators.length; i++) {
            total.put(creators[i], total.getOrDefault(creators[i], 0L) + views[i]);
            int oldMax = maxV.getOrDefault(creators[i], -1);
            if (views[i] > oldMax
                    || (views[i] == oldMax && ids[i].compareTo(best.get(creators[i])) < 0)) {
                maxV.put(creators[i], views[i]);
                best.put(creators[i], ids[i]);
            }
        }
        long maxTotal = 0;
        for (long v : total.values()) {
            maxTotal = Math.max(maxTotal, v);
        }
        List<List<String>> ans = new ArrayList<>();
        for (Map.Entry<String, Long> e : total.entrySet()) {
            if (e.getValue() == maxTotal) {
                List<String> row = new ArrayList<>();
                row.add(e.getKey());
                row.add(best.get(e.getKey()));
                ans.add(row);
            }
        }
        return ans;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        List<List<String>> r1 = sol.mostPopularCreator(
                new String[] {"alice", "bob", "alice", "chris"},
                new String[] {"one", "two", "three", "four"},
                new int[] {5, 10, 5, 4});
        check(contains(r1, "alice", "one") && contains(r1, "bob", "two") && r1.size() == 2,
                "2456 ex1 " + r1);
        List<List<String>> r1b = sol.mostPopularCreator2(
                new String[] {"alice", "bob", "alice", "chris"},
                new String[] {"one", "two", "three", "four"},
                new int[] {5, 10, 5, 4});
        check(contains(r1b, "alice", "one") && contains(r1b, "bob", "two") && r1b.size() == 2,
                "2456 ex1b " + r1b);
        List<List<String>> r2 = sol.mostPopularCreator(
                new String[] {"alice", "alice", "alice"},
                new String[] {"a", "b", "c"},
                new int[] {1, 2, 2});
        check(r2.size() == 1 && r2.get(0).get(1).equals("b"), "2456 ex2 " + r2);
        // 附加:多个创作者并列
        List<List<String>> r3 = sol.mostPopularCreator(
                new String[] {"x", "y"}, new String[] {"xa", "ya"}, new int[] {10, 10});
        check(r3.size() == 2, "2456 并列 " + r3);
        System.out.println("2456 全部 PASS");
    }

    private static boolean contains(List<List<String>> rows, String c, String id) {
        for (List<String> row : rows) {
            if (row.get(0).equals(c) && row.get(1).equals(id)) {
                return true;
            }
        }
        return false;
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

2457. Minimum Addition to Make Integer Beautiful(美丽整数的最小增量)

题目描述

给你两个正整数 n 和 target。如果整数每一位上的数字相加 ≤ target,则称其为美丽整数。

返回使 n + x 是美丽整数的最小非负整数 x(保证有解)。

示例 1

复制代码
`n=16, target=6` → `4`(16→20,数位和 2)。

示例 2

复制代码
`n=467, target=6` → `33`(467→500,数位和 5)。

示例 3

复制代码
`n=1, target=1` → `0`。

提示:

  • 1 <= n <= 10^12,1 <= target <= 150

解题思路

解法一:进位贪心(主提交版本)

  • 若 n 的数位和 ≤ target 则 x=0。
  • 否则从低位开始:把尾部若干位「进位清零」成 0(如 467 → 470 → 500),每进位一次数位和减小;
    直到数位和 ≤ target。答案 x = 新数 - 原数。
    解法二:逐次加 1 检查(对照,仅小值)
  • 直接用 BigInteger 语义:对 n 逐次加 1 判断数位和,小规模对拍。
    复杂度:
  • 时间复杂度:O(位数^2)(进位次数 ≤ 位数)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
public class Solution {
    // 解法一:进位贪心(主提交版本)
    public long makeIntegerBeautiful(long n, int target) {
        long x = 0;
        long base = 1; // 当前处理的位权
        while (digitSum(n) > target) {
            // 把 n 的最低非零位及以上变成 0(即进位)
            long rem = n % (base * 10);
            long add = (base * 10 - rem) % (base * 10);
            x += add;
            n += add;
            base *= 10;
        }
        return x;
    }

    // 解法二:逐次 +1(对照,小规模)
    public long makeIntegerBeautifulBrute(long n, int target) {
        long k = n, x = 0;
        while (digitSum(k) > target) {
            k++;
            x++;
        }
        return x;
    }

    private int digitSum(long v) {
        int s = 0;
        while (v > 0) {
            s += v % 10;
            v /= 10;
        }
        return s;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.makeIntegerBeautiful(16, 6) == 4, "2457 ex1");
        check(sol.makeIntegerBeautifulBrute(16, 6) == 4, "2457 ex1b");
        check(sol.makeIntegerBeautiful(467, 6) == 33, "2457 ex2");
        check(sol.makeIntegerBeautifulBrute(467, 6) == 33, "2457 ex2b");
        check(sol.makeIntegerBeautiful(1, 1) == 0, "2457 ex3");
        // 随机对拍(小规模)
        java.util.Random rnd = new java.util.Random(193);
        for (int t = 0; t < 500; t++) {
            long n = 1 + rnd.nextInt(100000);
            int tg = 1 + rnd.nextInt(30);
            long x = sol.makeIntegerBeautiful(n, tg), y = sol.makeIntegerBeautifulBrute(n, tg);
            if (x != y) {
                throw new AssertionError("2457 mismatch n=" + n + " tg=" + tg + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2457 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(位数^2)(进位次数 ≤ 位数)。
  • 空间复杂度:O(1)。

2458. Height of Binary Tree After Subtree Removal Queries(移除子树后的二叉树高度)

题目描述

给你一棵二叉树的根节点 root(n 个节点,值 1~n 互不相同)。另给数组 queries(m 个独立查询)。

第 i 个查询:移除以 queries[i] 的值为根节点的子树(不删根),返回移除后树的高度。

返回长度 m 的数组 answer。

示例 1

复制代码
`root=[1,3,4,2,null,6,5,null,null,null,null,null,7], queries=[4]` → `[2]`。

示例 2

复制代码
`root=[5,8,9,2,1,3,7,4,6], queries=[3,2,4,8]` → `[3,2,3,2]`。

提示:

  • 2 <= n <= 10^5,1 <= m <= min(n, 10^4),queries[i] != root.val

解题思路

解法一:两次 DFS 预处理(主提交版本)

  • 第一次 DFS 计算每个节点子树的深度 depth[v](以 v 为根的子树高度)。
  • 第二次 DFS 传递「兄弟路径的剩余高度」:遍历时维护一个 pathMax = 从根到当前节点路径上,
    除去当前节点子树外的最长路径长度。对每个节点,其祖先的最大贡献 = 祖先的兄弟子树深度 + 祖先深度,
    取路径上的最大值存到 ans[v]。
  • 实现:dfs2(u, depthSum),其中 depthSum = 根到 u 的路径上,「不经过 u 子树」的最远叶子距离
    (即各层兄弟树高 + 层号 的 max,以及 u 自身深度层号)。ans[u] = depthSum。
    转移:对子节点 c,depthSum' = max(depthSum, u 的深度层 + 兄弟子树高度)。
  • 复杂度 O(n + m)。
    解法二:逐查询暴力重建(对照,小规模)
  • 对每个查询标记移除节点后 BFS 求高度。
    复杂度:
  • 时间复杂度:O(n + m)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {
    private Map<Integer, Integer> heightOf; // 节点值 -> 子树高度
    private Map<Integer, Integer> ans;

    // 解法一:两遍 DFS(主提交版本)
    public int[] treeQueries(TreeNode root, int[] queries) {
        heightOf = new HashMap<>();
        ans = new HashMap<>();
        height(root); // 第一遍:子树高度
        dfs2(root, 0, 0); // 第二遍:每个节点被移除后的高度 = 除去该子树的最长路径
        int[] res = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            res[i] = ans.getOrDefault(queries[i], 0);
        }
        return res;
    }

    private int height(TreeNode u) {
        if (u == null) {
            return 0;
        }
        int h = 1 + Math.max(height(u.left), height(u.right));
        heightOf.put(u.val, h);
        return h;
    }

    private void dfs2(TreeNode u, int depth, int restMax) {
        ans.put(u.val, restMax);
        int leftH = u.left == null ? 0 : heightOf.get(u.left.val);
        int rightH = u.right == null ? 0 : heightOf.get(u.right.val);
        if (u.left != null) {
            // 去掉左子树后,能到达的最深层 = max(restMax, depth + 右子树高度)
            dfs2(u.left, depth + 1, Math.max(restMax, depth + rightH));
        }
        if (u.right != null) {
            dfs2(u.right, depth + 1, Math.max(restMax, depth + leftH));
        }
    }

    // 解法二:暴力重建(对照,小规模)
    public int[] treeQueriesBrute(TreeNode root, int[] queries) {
        Map<Integer, TreeNode> pos = new HashMap<>();
        collect(root, pos);
        int[] res = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            TreeNode removed = pos.get(queries[i]);
            if (removed == null) {
                res[i] = height(root) - 1;
            } else {
                // 标记移除:把 removed 子树与父节点断开(用值 0 标记不可达)
                res[i] = heightExcluding(root, removed) - 1;
            }
        }
        return res;
    }

    private void collect(TreeNode u, Map<Integer, TreeNode> pos) {
        if (u == null) {
            return;
        }
        pos.put(u.val, u);
        collect(u.left, pos);
        collect(u.right, pos);
    }

    private int heightExcluding(TreeNode root, TreeNode removed) {
        return hx(root, removed);
    }

    private int hx(TreeNode u, TreeNode removed) {
        if (u == null || u == removed) {
            return 0;
        }
        return 1 + Math.max(hx(u.left, removed), hx(u.right, removed));
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        TreeNode r1 = build(new Integer[] {1, 3, 4, 2, null, 6, 5, null, null, null, null, null, 7});
        check(Arrays.toString(sol.treeQueries(r1, new int[] {4})).equals("[2]"), "2458 ex1");
        check(Arrays.toString(sol.treeQueriesBrute(copy(r1), new int[] {4})).equals("[2]"), "2458 ex1b");
        TreeNode r2 = build(new Integer[] {5, 8, 9, 2, 1, 3, 7, 4, 6});
        check(Arrays.toString(sol.treeQueries(r2, new int[] {3, 2, 4, 8})).equals("[3, 2, 3, 2]"), "2458 ex2");
        check(Arrays.toString(sol.treeQueriesBrute(copy(r2), new int[] {3, 2, 4, 8})).equals("[3, 2, 3, 2]"),
                "2458 ex2b");
        // 随机小规模对拍:直接按 parent 数组构建任意二叉树
        java.util.Random rnd = new java.util.Random(197);
        for (int t = 0; t < 60; t++) {
            int n = 2 + rnd.nextInt(12);
            TreeNode rt = randTreeByParent(rnd, n); // 值 1..n
            List<Integer> vals = new ArrayList<>();
            collectVals(rt, vals);
            int qn = 1 + rnd.nextInt(Math.min(vals.size(), 6));
            int[] qs = new int[qn];
            int rootVal = rt.val;
            for (int i = 0; i < qn; i++) {
                int v;
                do {
                    v = vals.get(rnd.nextInt(vals.size()));
                } while (v == rootVal);
                qs[i] = v;
            }
            int[] x = sol.treeQueries(copy(rt), qs);
            int[] y = sol.treeQueriesBrute(copy(rt), qs);
            if (!java.util.Arrays.equals(x, y)) {
                throw new AssertionError("2458 mismatch t=" + t + " x=" + java.util.Arrays.toString(x)
                        + " y=" + java.util.Arrays.toString(y) + " qs=" + java.util.Arrays.toString(qs));
            }
        }
        System.out.println("2458 全部 PASS");
    }

    // 按 parent 数组构建:节点 i(值 i+1)的父为 rnd(i),随机放左/右
    private static TreeNode randTreeByParent(java.util.Random rnd, int n) {
        TreeNode[] nodes = new TreeNode[n];
        for (int i = 0; i < n; i++) {
            nodes[i] = new TreeNode(i + 1);
        }
        for (int i = 1; i < n; i++) {
            int p = rnd.nextInt(i);
            if (rnd.nextBoolean()) {
                nodes[p].left = nodes[i];
            } else {
                nodes[p].right = nodes[i];
            }
        }
        return nodes[0];
    }

    private static void collectVals(TreeNode u, List<Integer> vals) {
        if (u == null) {
            return;
        }
        vals.add(u.val);
        collectVals(u.left, vals);
        collectVals(u.right, vals);
    }

    private static TreeNode copy(TreeNode root) {
        if (root == null) {
            return null;
        }
        TreeNode c = new TreeNode(root.val);
        c.left = copy(root.left);
        c.right = copy(root.right);
        return c;
    }

    private static TreeNode build(Integer[] arr) {
        if (arr.length == 0) {
            return null;
        }
        TreeNode root = new TreeNode(arr[0]);
        Deque<TreeNode> q = new ArrayDeque<>();
        q.offer(root);
        int i = 1;
        while (i < arr.length) {
            TreeNode u = q.poll();
            if (u == null) {
                i += 2;
                continue;
            }
            if (i < arr.length && arr[i] != null) {
                u.left = new TreeNode(arr[i]);
                q.offer(u.left);
            }
            i++;
            if (i < arr.length && arr[i] != null) {
                u.right = new TreeNode(arr[i]);
                q.offer(u.right);
            }
            i++;
        }
        return root;
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }

    // 二叉树节点
    static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode(int x) {
            val = x;
        }
    }
}

复杂度分析

  • 时间复杂度:O(n + m)。
  • 空间复杂度:O(n)。

2459. Sort Array by Moving Items to Empty Space(通过移动项目到空白区域来排序数组)

题目描述

给定一个大小为 n 的整数数组 nums,包含 0 到 n-1 每个元素恰好一次。元素 0 代表空白区域。

一次操作:将任何 项目移动到空白区域。若项目的编号升序且空格在数组开头或结尾,则认为 nums 已排序

(例如 n=4 时 [0,1,2,3] 或 [1,2,3,0])。返回排序 nums 所需的最小操作数。

示例 1

复制代码
`nums = [4,2,0,3,1]` → `3`。

示例 2

复制代码
`nums = [1,2,3,4,0]` → `0`。

示例 3

复制代码
`nums = [1,0,2,4,3]` → `2`。

提示:

  • 2 <= n <= 10^5,0 <= nums[i] < n,值唯一

解题思路

解法一:置换环统计(主提交版本)

  • 任意排列可通过交换与 0 的位置置换排序。等价于在完全图中用「空位」作为交换中介。
  • 标准做法:把目标状态视为把每个值 x 放到位置 x-1(升序 [1,2,...,n-1,0])的置换,
    计算所有「错位环」的总代价:大小为 L 的环,若含 0 则代价 L-1,否则代价 L+1。
  • 但注意终态有两种(0 在头或尾),取两者最小。更简单直接的官方思路:
    计算(位置 0 即空位)置换环代价;目标状态两种都算。
    解法二:模拟 BFS(对照,小规模)
  • BFS 从初始状态到任一合法终态,求最小步数。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

代码实现

java 复制代码
import java.util.Arrays;

public class Solution {
    // 解法一:置换环(主提交版本)
    public int sortArray(int[] nums) {
        // 目标 A: [0,1,2,...,n-1](0 在开头)
        int costA = cycleCost(nums.clone(), false);
        // 目标 B: [1,2,...,n-1,0](0 在末尾)
        int costB = cycleCost(nums.clone(), true);
        return Math.min(costA, costB);
    }

    // tailZero=true 表示 0 在末尾的终态(位置 i 放值 i+1 对 i<n-1,位置 n-1 放 0)
    private int cycleCost(int[] nums, boolean tailZero) {
        int n = nums.length;
        boolean[] vis = new boolean[n];
        // 建置换:pos[v] = v 当前位置,终态时 nums[i] == target[i]
        int cost = 0;
        for (int i = 0; i < n; i++) {
            if (vis[i]) {
                continue;
            }
            // 找出 i 所在环长度
            int cur = i, len = 0;
            boolean hasZero = false;
            while (!vis[cur]) {
                vis[cur] = true;
                len++;
                if (nums[cur] == 0) {
                    hasZero = true;
                }
                // 走到目标位置
                int targetPos;
                if (!tailZero) {
                    targetPos = nums[cur]; // 值为 x 应去位置 x
                } else {
                    targetPos = (nums[cur] == 0) ? n - 1 : nums[cur] - 1;
                }
                cur = targetPos;
            }
            if (len > 1) {
                cost += hasZero ? len - 1 : len + 1;
            }
        }
        return cost;
    }

    // 解法二:BFS(对照,小规模)
    public int sortArrayBrute(int[] nums) {
        int n = nums.length;
        String start = Arrays.toString(nums).replaceAll("[\\[\\], ]", "");
        String t1 = "0" + seq(n) + ""; // 0 开头
        String t2 = seq(n) + "0"; // 0 结尾(seq 生成 1..n-1)
        // BFS
        java.util.ArrayDeque<String> q = new java.util.ArrayDeque<>();
        java.util.Map<String, Integer> dist = new java.util.HashMap<>();
        dist.put(start, 0);
        q.add(start);
        while (!q.isEmpty()) {
            String s = q.poll();
            if (s.equals(t1) || s.equals(t2)) {
                return dist.get(s);
            }
            int z = s.indexOf('0');
            for (int i = 0; i < n; i++) {
                if (i == z) {
                    continue;
                }
                char[] c = s.toCharArray();
                char tmp = c[z];
                c[z] = c[i];
                c[i] = tmp;
                String ns = new String(c);
                if (!dist.containsKey(ns)) {
                    dist.put(ns, dist.get(s) + 1);
                    q.add(ns);
                }
            }
        }
        return -1;
    }

    private String seq(int n) {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < n; i++) {
            sb.append(i);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(sol.sortArray(new int[] {4, 2, 0, 3, 1}) == 3, "2459 ex1");
        check(sol.sortArrayBrute(new int[] {4, 2, 0, 3, 1}) == 3, "2459 ex1b");
        check(sol.sortArray(new int[] {1, 2, 3, 4, 0}) == 0, "2459 ex2");
        check(sol.sortArrayBrute(new int[] {1, 2, 3, 4, 0}) == 0, "2459 ex2b");
        check(sol.sortArray(new int[] {1, 0, 2, 4, 3}) == 2, "2459 ex3");
        check(sol.sortArrayBrute(new int[] {1, 0, 2, 4, 3}) == 2, "2459 ex3b");
        // 随机小规模对拍
        java.util.Random rnd = new java.util.Random(199);
        for (int t = 0; t < 200; t++) {
            int n = 2 + rnd.nextInt(6);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = i;
            }
            // 随机洗牌
            for (int i = n - 1; i > 0; i--) {
                int j = rnd.nextInt(i + 1);
                int tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
            int x = sol.sortArray(a.clone()), y = sol.sortArrayBrute(a.clone());
            if (x != y) {
                throw new AssertionError("2459 mismatch t=" + t + " a=" + Arrays.toString(a) + " x=" + x + " y=" + y);
            }
        }
        System.out.println("2459 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

2460. Apply Operations to an Array(对数组执行操作)

题目描述

给你一个下标从 0 开始的数组 nums(大小 n,非负整数)。执行 n-1 步操作(第 i 步,i 从 0 计数):

  • 若 nums[i] == nums[i+1],则 nums[i] 变为原来的 2 倍、nums[i+1] 变为 0;否则跳过。
    操作应当依次有序执行。执行完后把所有 0 移到数组末尾,返回结果数组。

示例 1

复制代码
`nums = [1,2,2,1,1,0]` → `[1,4,2,0,0,0]`。

示例 2

复制代码
`nums = [0,1]` → `[1,0]`。

提示:

  • 2 <= nums.length <= 2000,0 <= nums[i] <= 1000

解题思路

解法一:就地模拟(主提交版本)

  • 第一遍按规则就地修改(注意修改后 numsi+1 变 0 不再参与后续判断,但 i 会继续走,
    第 i+1 步检查的是新 numsi+1 与 numsi+2,符合题意)。
  • 第二遍双指针把非零数前移,末尾补 0。
    解法二:显式零数组(对照)
  • 用独立 list 收集非零元素再拼接,逻辑直观。
    复杂度:
  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

代码实现

java 复制代码
import java.util.Arrays;

public class Solution {
    // 解法一:就地模拟(主提交版本)
    public int[] applyOperations(int[] nums) {
        int n = nums.length;
        for (int i = 0; i < n - 1; i++) {
            if (nums[i] == nums[i + 1]) {
                nums[i] *= 2;
                nums[i + 1] = 0;
            }
        }
        int[] ans = new int[n];
        int idx = 0;
        for (int x : nums) {
            if (x != 0) {
                ans[idx++] = x;
            }
        }
        return ans;
    }

    // 解法二:双指针前移(对照)
    public int[] applyOperations2(int[] nums) {
        int n = nums.length;
        for (int i = 0; i < n - 1; i++) {
            if (nums[i] == nums[i + 1]) {
                nums[i] *= 2;
                nums[i + 1] = 0;
            }
        }
        int p = 0;
        for (int i = 0; i < n; i++) {
            if (nums[i] != 0) {
                nums[p++] = nums[i];
            }
        }
        while (p < n) {
            nums[p++] = 0;
        }
        return nums;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        check(Arrays.toString(sol.applyOperations(new int[] {1, 2, 2, 1, 1, 0})).equals("[1, 4, 2, 0, 0, 0]"),
                "2460 ex1");
        check(Arrays.toString(sol.applyOperations2(new int[] {1, 2, 2, 1, 1, 0})).equals("[1, 4, 2, 0, 0, 0]"),
                "2460 ex1b");
        check(Arrays.toString(sol.applyOperations(new int[] {0, 1})).equals("[1, 0]"), "2460 ex2");
        check(Arrays.toString(sol.applyOperations2(new int[] {0, 1})).equals("[1, 0]"), "2460 ex2b");
        // 随机对拍
        java.util.Random rnd = new java.util.Random(211);
        for (int t = 0; t < 500; t++) {
            int n = 2 + rnd.nextInt(20);
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = rnd.nextInt(5);
            }
            int[] x = sol.applyOperations(a.clone()), y = sol.applyOperations2(a.clone());
            if (!Arrays.equals(x, y)) {
                throw new AssertionError("2460 mismatch t=" + t + " x=" + Arrays.toString(x)
                        + " y=" + Arrays.toString(y));
            }
        }
        System.out.println("2460 全部 PASS");
    }

    private static void check(boolean ok, String msg) {
        if (!ok) {
            throw new AssertionError(msg);
        }
        System.out.println("PASS " + msg);
    }
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。
相关推荐
阿屿1 小时前
从零手搓 AI 伴侣(二):好感度通胀危机,与它的「货币政策」
java·人工智能
灿飞1 小时前
Kotlin与Java虚拟机十一:类与属性:构造顺序、backing field 与显式 backing field
java·后端·kotlin
aiks_yuan1 小时前
40亿个QQ号 1G内存怎么去重:Java BitSet 装不下,long 数组位图 512MB 实测 46 秒(JDK 17)
java
文丑颜不良啊1 小时前
LeetCode2521~2540题目解析
java·leetcode
杨凯凡1 小时前
Spring Framework 7 HTTP Interface 实战:声明式 RestClient、超时与测试
java·spring·http
吴名氏.2 小时前
Java转AI Agent教程(第十二周)检索与切分
java·开发语言·人工智能·ai·ai agent
瑞雪兆丰年兮2 小时前
JavaWeb-day10-SpringBootWeb案例
java·开发语言·javaweb·web
Rain的Java大神之路3 小时前
你背的Spring答案,全是5.x时代的:7.0.8源码一翻,80%的博客都过时了
java·后端·面试
这个昵称不容易3 小时前
考试报名系统的设计与实现(SpringBoot + MySQL + Vue 三角色平台)
java·springboot·毕设