2784.检查数组是否是好的

目标

给你一个整数数组 nums ,如果它是数组 base[n] 的一个排列,我们称它是个 好 数组。

base[n] = [1, 2, ..., n - 1, n, n] (换句话说,它是一个长度为 n + 1 且包含 1 到 n - 1 恰好各一次,包含 n 两次的一个数组)。比方说,base[1] = [1, 1] ,base[3] = [1, 2, 3, 3] 。

如果数组是一个好数组,请你返回 true ,否则返回 false 。

注意:数组的排列是这些数字按任意顺序排布后重新得到的数组。

示例 1:

输入:nums = [2, 1, 3]
输出:false
解释:因为数组的最大元素是 3 ,唯一可以构成这个数组的 base[n] 对应的 n = 3 。但是 base[3] 有 4 个元素,但数组 nums 只有 3 个元素,所以无法得到 base[3] = [1, 2, 3, 3] 的排列,所以答案为 false 。

示例 2:

输入:nums = [1, 3, 3, 2]
输出:true
解释:因为数组的最大元素是 3 ,唯一可以构成这个数组的 base[n] 对应的 n = 3 ,可以看出数组是 base[3] = [1, 2, 3, 3] 的一个排列(交换 nums 中第二个和第四个元素)。所以答案为 true 。

示例 3:

输入:nums = [1, 1]
输出:true
解释:因为数组的最大元素是 1 ,唯一可以构成这个数组的 base[n] 对应的 n = 1,可以看出数组是 base[1] = [1, 1] 的一个排列。所以答案为 true 。

示例 4:

输入:nums = [3, 4, 4, 1, 2, 1]
输出:false
解释:因为数组的最大元素是 4 ,唯一可以构成这个数组的 base[n] 对应的 n = 4 。但是 base[n] 有 5 个元素而 nums 有 6 个元素。所以答案为 false 。

说明:

  • 1 <= nums.length <= 100
  • 1 <= num[i] <= 200

思路

有一个长度为 n + 1 的数组 nums,判断它是否是数组 [1, 2, 3, ……, n - 1, n, n] 的一个排列。

注意到目标数组的元素与下标的关系为 nums[i] = i + 1, i < n,特殊处理后两个位置,使用一个指针 p 指向下标 n - 1。遍历数组 numsn 个元素,如果当前位置的元素值不满足条件,就将其与满足条件的位置(当 nums[i] < n 时,取 nums[i] - 1,当 nums[i] == n 时取 p,当 nums[i] > n 直接返回 false)上的元素值交换,如果目标位置的元素值已经满足条件或者 n 的出现次数超过两次,返回 false。最后返回 nums[n] == n 即可。

代码


/**
 * @date 2026-05-14 9:40
 */
public class IsGood2784 {

    public boolean isGood(int[] nums) {
        int n = nums.length - 1;
        int p = n - 1;
        for (int i = 0; i < n; i++) {
            while (nums[i] != i + 1) {
                int index = nums[i] - 1;
                if (nums[i] > n || (nums[i] < n && nums[index] == nums[i]) || (nums[i] == n && p > n)) {
                    return false;
                }
                if (nums[i] == n) {
                    index = p++;
                }
                int tmp = nums[index];
                nums[index] = nums[i];
                nums[i] = tmp;
            }
        }
        return nums[n] == n;
    }
}

性能

1674.使数组互补的最少操作次数

目标

给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。

如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。

返回使数组 互补 的 最少 操作次数。

示例 1:

输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。

示例 2:

输入:nums = [1,2,2,1], limit = 2
输出:2
解释:经过 2 次操作,你可以将数组 nums 变成 [2,2,2,2] 。你不能将任何数字变更为 3 ,因为 3 > limit 。

示例 3:

输入:nums = [1,2,1,2], limit = 2
输出:0
解释:nums 已经是互补的。

说明:

  • n == nums.length
  • 2 <= n <= 10^5
  • 1 <= nums[i] <= limit <= 10^5
  • n 是偶数。

思路

有一个长度为偶数的数组 nums 和一个整数 limit,每次操作可以将任意元素替换为 [1, limit] 中的任意整数,求使得 nums 互补的最少操作次数。所谓互补指 nums[i] + nums[n - 1 - i] 的和都相等。

由于 1 <= nums[i] <= limit,每一对元素和都可以变成 [2, 2 * limit] 中的任意数字。

a = nums[i], b = nums[n - 1 - i], sum = a + b,如果只操作一次,考虑左侧 a -> 1 或者 b -> 1sum 最多变化 max(a, b) - 1;右侧 a -> limit 或者 b -> limitsum 最多变化 limit - min(a, b)

  • sum 变成区间 [sum - (max(a, b) - 1), sum - 1], [sum + 1, sum + limit - min(a, b)] 内的值需要操作一次
  • 变成 sum 的操作次数不变
  • 其它 [2, sum - (max(a, b) - 1) - 1], [sum + limit - min(a, b) + 1, 2 * limit] 需要操作两次

使用差分数组来记录每对元素和变成目标和所需的操作次数,最后遍历所有目标和,取操作次数最小的即可。

代码


/**
 * @date 2026-05-13 10:13
 */
public class MinMoves1674 {

    public int minMoves(int[] nums, int limit) {
        int n = nums.length;
        int max = 2 * limit;
        int[] diff = new int[max + 2];
        for (int i = 0; i < n / 2; i++) {
            int a = nums[i];
            int b = nums[n - 1 - i];
            int sum = a + b;
            int l = sum - (Math.max(a, b) - 1);
            int r = sum + (limit - Math.min(a, b));
            diff[2] += 2;
            diff[Math.max(2, l)]--;
            diff[sum]--;
            diff[Math.min(sum + 1, max + 1)]++;
            diff[Math.min(r + 1, max + 1)]++;
        }
        int sum = 0, res = Integer.MAX_VALUE;
        for (int i = 2; i <= max; i++) {
            sum += diff[i];
            res = Math.min(sum, res);
        }
        return res;
    }

}

性能

3629.通过质数传送到达终点的最少跳跃次数

目标

给你一个长度为 n 的整数数组 nums。

你从下标 0 开始,目标是到达下标 n - 1。

在任何下标 i 处,你可以执行以下操作之一:

  • 移动到相邻格子:跳到下标 i + 1 或 i - 1,如果该下标在边界内。
  • 质数传送:如果 nums[i] 是一个质数 p,你可以立即跳到任何满足 nums[j] % p == 0 的下标 j 处,且下标 j != i 。

返回到达下标 n - 1 所需的 最少 跳跃次数。

质数 是一个大于 1 的自然数,只有两个因子,1 和它本身。

示例 1:

输入: nums = [1,2,4,6]
输出: 2
解释:
一个最优的跳跃序列是:
从下标 i = 0 开始。向相邻下标 1 跳一步。
在下标 i = 1,nums[1] = 2 是一个质数。因此,我们传送到索引 i = 3,因为 nums[3] = 6 可以被 2 整除。
因此,答案是 2。

示例 2:

输入: nums = [2,3,4,7,9]
输出: 2
解释:
一个最优的跳跃序列是:
从下标 i = 0 开始。向相邻下标 i = 1 跳一步。
在下标 i = 1,nums[1] = 3 是一个质数。因此,我们传送到下标 i = 4,因为 nums[4] = 9 可以被 3 整除。
因此,答案是 2。

示例 3:

输入: nums = [4,6,5,8]
输出: 3
解释:
由于无法进行传送,我们通过 0 → 1 → 2 → 3 移动。因此,答案是 3。

说明:

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

思路

有一个数组 nums,从下标 0 开始移动,目标是到达下标 n - 1。每次移动可以移动到相邻的位置,如果 nums[i] 是质数,可以直接移动到满足 nums[j] % nums[i] == 0 的位置。返回最少移动次数。

枚举 2 ~ MAX,为每一个数记录质因子,得到哈希表 (num,primeFactorList),枚举数组中的元素,获取 primeFactorList,为其中的每一个质数建立一个跳转列表 jumpIndexList,将当前下标加进去。从 0 开始 bfs,记录跳转次数,直到到达下标 n - 1

代码


/**
 * @date 2026-05-08 16:55
 */
public class MinJumps3629 {

    public static final int MAX = 1000000;
    public static Map<Integer, List<Integer>> primeFactorMap = new HashMap<>();

    static {
        primeFactorMap.computeIfAbsent(1, k -> new ArrayList<>());
        for (int i = 2; i <= MAX; i++) {
            if (primeFactorMap.get(i) == null) {
                for (int j = i; j <= MAX; j += i) {
                    primeFactorMap.computeIfAbsent(j, k -> new ArrayList<>()).add(i);
                }
            }
        }
    }

    public int minJumps(int[] nums) {
        int n = nums.length;
        Map<Integer, List<Integer>> jumpIndexList = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            List<Integer> primeFactors = primeFactorMap.get(nums[i]);
            for (Integer prime : primeFactors) {
                jumpIndexList.computeIfAbsent(prime, k -> new ArrayList<>()).add(i);
            }
        }
        boolean[] visited = new boolean[n];
        Deque<Integer> q = new ArrayDeque<>();
        q.offer(0);
        int res = 0;
        here:
        while (!q.isEmpty()) {
            int size = q.size();
            for (int i = 0; i < size; i++) {
                Integer index = q.poll();
                if (visited[index]) {
                    continue;
                }
                visited[index] = true;
                if (index == n - 1) {
                    break here;
                }
                int next = index + 1;
                int prev = index - 1;
                if (next < n) {
                    q.offer(next);
                }
                if (prev >= 0) {
                    q.offer(prev);
                }
                if (jumpIndexList.get(nums[index]) != null) {
                    q.addAll(jumpIndexList.get(nums[index]));
                    jumpIndexList.get(nums[index]).clear();
                }
            }
            res++;
        }
        return res;
    }

}

性能

788.旋转数字

目标

我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数。要求每位数字都要被旋转。

如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。0, 1, 和 8 被旋转后仍然是它们自己;2 和 5 可以互相旋转成对方(在这种情况下,它们以不同的方向旋转,换句话说,2 和 5 互为镜像);6 和 9 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。

现在我们有一个正整数 N, 计算从 1 到 N 中有多少个数 X 是好数?

示例:

输入: 10
输出: 4
解释: 
在[1, 10]中有四个好数: 2, 5, 6, 9。
注意 1 和 10 不是好数, 因为他们在旋转之后不变。

说明:

  • N 的取值范围是 [1, 10000]。

思路

判断 1 ~ N 的数字中,不含 3 4 7,且一定要含 2 5 6 9 的数字个数。

// todo:数位 dp

代码


/**
 * @date 2026-05-06 16:45
 */
public class RotatedDigits788 {

    public int rotatedDigits(int n) {
        int res = 0;
        Set<Integer> set = new HashSet<>();
        set.add(2);
        set.add(5);
        set.add(6);
        set.add(9);
        for (int i = 1; i <= n; i++) {
            int num = i;
            boolean valid = false;
            while (num > 0) {
                int d = num % 10;
                if (d == 3 || d == 4 || d == 7) {
                    break;
                }
                if (set.contains(d)){
                    valid = true;
                }
                num /= 10;
            }
            if (num == 0 && valid) {
                res++;
            }
        }
        return res;
    }
}

性能

1391.检查网格中是否存在有效路径

目标

给你一个 m x n 的网格 grid。网格里的每个单元都代表一条街道。grid[i][j] 的街道可以是:

  • 1 表示连接左单元格和右单元格的街道。
  • 2 表示连接上单元格和下单元格的街道。
  • 3 表示连接左单元格和下单元格的街道。
  • 4 表示连接右单元格和下单元格的街道。
  • 5 表示连接左单元格和上单元格的街道。
  • 6 表示连接右单元格和上单元格的街道。

你最开始从左上角的单元格 (0,0) 开始出发,网格中的「有效路径」是指从左上方的单元格 (0,0) 开始、一直到右下方的 (m-1,n-1) 结束的路径。该路径必须只沿着街道走。

注意:你 不能 变更街道。

如果网格中存在有效的路径,则返回 true,否则返回 false 。

示例 1:

输入:grid = [[2,4,3],[6,5,2]]
输出:true
解释:如图所示,你可以从 (0, 0) 开始,访问网格中的所有单元格并到达 (m - 1, n - 1) 。

示例 2:

输入:grid = [[1,2,1],[1,2,1]]
输出:false
解释:如图所示,单元格 (0, 0) 上的街道没有与任何其他单元格上的街道相连,你只会停在 (0, 0) 处。

示例 3:

输入:grid = [[1,1,2]]
输出:false
解释:你会停在 (0, 1),而且无法到达 (0, 2) 。

示例 4:

输入:grid = [[1,1,1,1,1,1,3]]
输出:true

示例 5:

输入:grid = [[2],[2],[2],[2],[2],[2],[6]]
输出:true

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • 1 <= grid[i][j] <= 6

思路

有一个二维矩阵 grid,每个单元格的值都代表一种街道类型,判断能否从左上角出发沿着街道走到右下角。

根据当前的街道类型,初始化可走的方向,同时判断下一个位置是否超出了边界,是否访问过,是否与当前街道连通。

代码


/**
 * @date 2026-04-27 9:08
 */
public class HasValidPath1391 {

    public boolean hasValidPath(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        boolean[][] visited = new boolean[m][n];
        return dfs(0, 0, new int[]{1, 2, 3, 4, 5, 6}, visited, grid);
    }

    public static int[][][] directions = new int[][][]{
            {{0, 0}, {0, 0}},
            {{0, -1}, {0, 1}},
            {{-1, 0}, {1, 0}},
            {{0, -1}, {1, 0}},
            {{0, 1}, {1, 0}},
            {{0, -1}, {-1, 0}},
            {{0, 1}, {-1, 0}},
    };

    public static int[][][] allowedTypes = new int[][][]{
            {{}},
            {{1, 4, 6}, {1, 3, 5}},
            {{2, 3, 4}, {2, 5, 6}},
            {{1, 4, 6}, {2, 5, 6}},
            {{1, 3, 5}, {2, 5, 6}},
            {{1, 4, 6}, {2, 3, 4}},
            {{1, 3, 5}, {2, 3, 4}},
    };

    public boolean dfs(int row, int col, int[] types, boolean[][] visited, int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        if (row == m || row < 0 || col == n || col < 0 || visited[row][col]) {
            return false;
        }
        visited[row][col] = true;
        int type = grid[row][col];
        boolean valid = false;
        for (int t : types) {
            if (t == type) {
                valid = true;
                break;
            }
        }
        if (!valid) {
            return false;
        }
        boolean res = row == m - 1 && col == n - 1;
        for (int i = 0; i < directions[type].length; i++) {
            res = res || dfs(row + directions[type][i][0], col + directions[type][i][1], allowedTypes[type][i], visited, grid);
        }
        return res;
    }

}

性能

2615.等值距离和

目标

给你一个下标从 0 开始的整数数组 nums 。现有一个长度等于 nums.length 的数组 arr 。对于满足 nums[j] == nums[i] 且 j != i 的所有 j ,arr[i] 等于所有 |i - j| 之和。如果不存在这样的 j ,则令 arr[i] 等于 0 。

返回数组 arr 。

示例 1:

输入:nums = [1,3,1,1,2]
输出:[5,0,3,4,0]
解释:
i = 0 ,nums[0] == nums[2] 且 nums[0] == nums[3] 。因此,arr[0] = |0 - 2| + |0 - 3| = 5 。 
i = 1 ,arr[1] = 0 因为不存在值等于 3 的其他下标。
i = 2 ,nums[2] == nums[0] 且 nums[2] == nums[3] 。因此,arr[2] = |2 - 0| + |2 - 3| = 3 。
i = 3 ,nums[3] == nums[0] 且 nums[3] == nums[2] 。因此,arr[3] = |3 - 0| + |3 - 2| = 4 。 
i = 4 ,arr[4] = 0 因为不存在值等于 2 的其他下标。

示例 2:

输入:nums = [0,5,3]
输出:[0,0,0]
解释:因为 nums 中的元素互不相同,对于所有 i ,都有 arr[i] = 0 。

说明:

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

思路

有一个整数数组 nums,返回一个等长数组 arrarr[i] 等于所有与 nums[i] 相等的 nums[j] 到下标 i 的距离之和,即 Σ|i - j|,如果不存在这样的 j 则距离为 0

使用哈希表将元素分组,假定某一组内的元素为 a0 < a1 < …… < a_(m - 1),令 S0 = Σ_(j ∈ [0, m - 1])(aj - a0),考虑 S1 - S0,原来所有元素到 a0 的距离变成了所有元素到 a1 的距离:组内下标小于 1 的距离都增加了 a1 - a0,组内下标大于等于 1 的距离都减少了 a1 - a0

更一般的情况 Si - S_(i - 1),组内下标小于 i 的元素到 a_(i - 1) 的距离都增加了 a_i - a_(i - 1),组内下标大于等于 i 的元素到 a_(i - 1) 的距离都减小了 a_i - a_(i - 1)。组内下标小于 i 的元素有 i 个,组内下标大于等于 i 的元素有 m - i 个,Si - S_(i - 1) = i * (ai - a_(i - 1)) - (m - i) * (ai - a_(i - 1)) = (2 * i - m) * (ai - a_(i - 1))

代码


/**
 * @date 2026-04-23 9:46
 */
public class Distance2615 {

    public long[] distance(int[] nums) {
        int n = nums.length;
        long[] res = new long[n];
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        for (List<Integer> list : map.values()) {
            if (list.size() > 1) {
                long s = 0L;
                int start = list.get(0);
                for (int i = 1; i < list.size(); i++) {
                    s += list.get(i) - start;
                }
                res[start] = s;
                for (int i = 1; i < list.size(); i++) {
                    res[list.get(i)] = res[list.get(i - 1)] + (2 * i - list.size()) * (list.get(i) - list.get(i - 1));
                }
            }
        }
        return res;
    }
}

性能

1722.执行交换操作后的最小汉明距离

目标

给你两个整数数组 source 和 target ,长度都是 n 。还有一个数组 allowedSwaps ,其中每个 allowedSwaps[i] = [ai, bi] 表示你可以交换数组 source 中下标为 ai 和 bi(下标从 0 开始)的两个元素。注意,你可以按 任意 顺序 多次 交换一对特定下标指向的元素。

相同长度的两个数组 source 和 target 间的 汉明距离 是元素不同的下标数量。形式上,其值等于满足 source[i] != target[i] (下标从 0 开始)的下标 i(0 <= i <= n-1)的数量。

在对数组 source 执行 任意 数量的交换操作后,返回 source 和 target 间的 最小汉明距离 。

示例 1:

输入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
输出:1
解释:source 可以按下述方式转换:
- 交换下标 0 和 1 指向的元素:source = [2,1,3,4]
- 交换下标 2 和 3 指向的元素:source = [2,1,4,3]
source 和 target 间的汉明距离是 1 ,二者有 1 处元素不同,在下标 3 。

示例 2:

输入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
输出:2
解释:不能对 source 执行交换操作。
source 和 target 间的汉明距离是 2 ,二者有 2 处元素不同,在下标 1 和下标 2 。

示例 3:

输入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
输出:0

说明:

  • n == source.length == target.length
  • 1 <= n <= 10^5
  • 1 <= source[i], target[i] <= 10^5
  • 0 <= allowedSwaps.length <= 10^5
  • allowedSwaps[i].length == 2
  • 0 <= ai, bi <= n - 1
  • ai != bi

思路

有两个长度相等的数组 sourcetarget,另有一个交换数组 allowedSwaps[i] = [ai, bi],表示可以交换 source[ai]source[bi]。求按照任意顺序交换任意次之后,sourcetarget 的最小汉明距离。汉明距离指满足 source[i] != target[i]i 的个数。

记录可以交换的连通元素的频次,然后比较对应位置上 target 的元素,最小的汉明距离就是对应不上的元素个数。

使用并查集记录连通分量,为每一个连通分量维护一个哈希表,记录元素出现的频次。枚举 target,找到所属的连通分量,将对应连通分量的元素频次 -1,如果最终频次小于 0 那么汉明距离 +1

代码


/**
 * @date 2026-04-21 9:12
 */
public class MinimumHammingDistance1722 {

    public class UnionFind {
        int[] fa;

        public UnionFind(int n) {
            this.fa = new int[n];
            Arrays.setAll(this.fa, i -> i);
        }

        public int find(int e) {
            if (e != fa[e]) {
                fa[e] = find(fa[e]);
            }
            return fa[e];
        }

        public void merge(int x, int y) {
            int a = find(x);
            int b = find(y);
            if (a < b) {
                fa[b] = a;
            } else if (a > b) {
                fa[a] = b;
            }
        }
    }

    public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
        int n = source.length;
        UnionFind uf = new UnionFind(n);
        for (int[] swap : allowedSwaps) {
            uf.merge(swap[0], swap[1]);
        }
        Map<Integer, Map<Integer, Integer>> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            int c = uf.find(i);
            map.putIfAbsent(c, new HashMap<>());
            map.get(c).merge(source[i], 1, Integer::sum);
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            int c = uf.find(i);
            map.get(c).merge(target[i], -1, Integer::sum);
            if (map.get(c).get(target[i]) < 0) {
                res++;
            }
        }
        return res;
    }

}

性能

3761.镜像对之间最小绝对距离

目标

给你一个整数数组 nums。

镜像对 是指一对满足下述条件的下标 (i, j):

  • 0 <= i < j < nums.length,并且
  • reverse(nums[i]) == nums[j],其中 reverse(x) 表示将整数 x 的数字反转后形成的整数。反转后会忽略前导零,例如 reverse(120) = 21。

返回任意镜像对的下标之间的 最小绝对距离。下标 i 和 j 之间的绝对距离为 abs(i - j)。

如果不存在镜像对,返回 -1。

示例 1:

输入: nums = [12,21,45,33,54]
输出: 1
解释:
镜像对为:
(0, 1),因为 reverse(nums[0]) = reverse(12) = 21 = nums[1],绝对距离为 abs(0 - 1) = 1。
(2, 4),因为 reverse(nums[2]) = reverse(45) = 54 = nums[4],绝对距离为 abs(2 - 4) = 2。
所有镜像对中的最小绝对距离是 1。

示例 2:

输入: nums = [120,21]
输出: 1
解释:
只有一个镜像对 (0, 1),因为 reverse(nums[0]) = reverse(120) = 21 = nums[1]。
最小绝对距离是 1。

示例 3:

输入: nums = [21,120]
输出: -1
解释:
数组中不存在镜像对。

说明:

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

思路

有一个整数数组 nums,镜像对指满足条件的下标对 (i, j)0 <= i < j < nums.length && reverse(nums[i]) == nums[j]reverse(x)x 的十进制表示的顺序反转并且去掉前导零。返回镜像对的最小距离。

维护左边的镜像值,枚举右,判断已维护的镜像值中是否存在当前值,取距离的最小值即可。

代码


/**
 * @date 2026-04-17 8:49
 */
public class MinMirrorPairDistance3761 {

    public int minMirrorPairDistance(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            int num = nums[i];
            if (map.get(num) != null) {
                res = Math.min(res, i - map.get(num));
            }
            int mirror = 0;
            int base = 10;
            while (num > 0) {
                mirror = (num % 10) + mirror * base;
                num /= 10;
            }
            map.put(mirror, i);
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }

}

性能

3488.距离最小相等元素查询

目标

给你一个 环形 数组 nums 和一个数组 queries 。

对于每个查询 i ,你需要找到以下内容:

  • 数组 nums 中下标 queries[i] 处的元素与 任意 其他下标 j(满足 nums[j] == nums[queries[i]])之间的 最小 距离。如果不存在这样的下标 j,则该查询的结果为 -1 。

返回一个数组 answer,其大小与 queries 相同,其中 answer[i] 表示查询i的结果。

示例 1:

输入: nums = [1,3,1,4,1,3,2], queries = [0,3,5]
输出: [2,-1,3]
解释:
查询 0:下标 queries[0] = 0 处的元素为 nums[0] = 1 。最近的相同值下标为 2,距离为 2。
查询 1:下标 queries[1] = 3 处的元素为 nums[3] = 4 。不存在其他包含值 4 的下标,因此结果为 -1。
查询 2:下标 queries[2] = 5 处的元素为 nums[5] = 3 。最近的相同值下标为 1,距离为 3(沿着循环路径:5 -> 6 -> 0 -> 1)。

示例 2:

输入: nums = [1,2,3,4], queries = [0,1,2,3]
输出: [-1,-1,-1,-1]
解释:
数组 nums 中的每个值都是唯一的,因此没有下标与查询的元素值相同。所有查询的结果均为 -1。

说明:

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

思路

有一个环形数组 nums 和一个查询数组 queries,返回 元素值与 queries[i] 相同的其它元素之间的最小距离,如果不存在则结果为 -1,返回查询结果数组。

根据元素值分组,分组内记录下标。针对每个查询,二分查找元素在分组中的位置,比较前后下标的距离,如果分组只有一个元素,返回 -1。需要特殊处理首与尾的最近距离。

也可以分组后预处理每个位置的最近距离,统一计算前后的最近距离,查询时直接取结果即可。注意循环数组的距离处理,这里直接将超出的距离映射回了原数组下标,因此最近距离需要取 min(d, n - d),其中 d = abs(i - j) 表示下标 ij 的距离。

代码


/**
 * @date 2026-04-16 8:55
 */
public class SolveQueries3488 {

    public List<Integer> solveQueries_v1(int[] nums, int[] queries) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        int[] dis = new int[n];
        for (List<Integer> list : map.values()) {
            int size = list.size();
            for (int i = 0; i < size; i++) {
                int index = list.get(i);
                if (size >= 2) {
                    int prev = Math.abs(index - list.get((size + i - 1) % size));
                    int next = Math.abs(list.get((i + 1) % size) - index);
                    dis[index] = Math.min(Math.min(prev, n - prev), Math.min(next, n - next));
                } else {
                    dis[index] = -1;
                }
            }
        }
        List<Integer> res = new ArrayList<>(queries.length);
        for (int query : queries) {
            res.add(dis[query]);
        }
        return res;
    }

}

性能

3741.三个相等元素之间的最小距离II

目标

给你一个整数数组 nums。

如果满足 nums[i] == nums[j] == nums[k],且 (i, j, k) 是 3 个 不同 下标,那么三元组 (i, j, k) 被称为 有效三元组 。

有效三元组 的 距离 被定义为 abs(i - j) + abs(j - k) + abs(k - i),其中 abs(x) 表示 x 的 绝对值 。

返回一个整数,表示 有效三元组 的 最小 可能距离。如果不存在 有效三元组 ,返回 -1。

示例 1:

输入: nums = [1,2,1,1,3]
输出: 6
解释:
最小距离对应的有效三元组是 (0, 2, 3) 。
(0, 2, 3) 是一个有效三元组,因为 nums[0] == nums[2] == nums[3] == 1。它的距离为 abs(0 - 2) + abs(2 - 3) + abs(3 - 0) = 2 + 1 + 3 = 6。

示例 2:

输入: nums = [1,1,2,3,2,1,2]
输出: 8
解释:
最小距离对应的有效三元组是 (2, 4, 6) 。
(2, 4, 6) 是一个有效三元组,因为 nums[2] == nums[4] == nums[6] == 2。它的距离为 abs(2 - 4) + abs(4 - 6) + abs(6 - 2) = 2 + 2 + 4 = 8。

示例 3:

输入: nums = [1]
输出: -1
解释:
不存在有效三元组,因此答案为 -1。

说明:

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

思路

参考 3740.三个相等元素之间的最小距离I,数据范围变大了,当时写的就是 O(n) 的解法。

代码


/**
 * @date 2026-04-11 8:46
 */
public class MinimumDistance3740 {

    public int minimumDistance(int[] nums) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        int res = Integer.MAX_VALUE;
        for (List<Integer> list : map.values()) {
            int size = list.size();
            if (size < 3) {
                continue;
            }
            for (int i = 2; i < size; i++) {
                res = Math.min(res, list.get(i) * 2 - list.get(i - 2) * 2);
            }
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }
}

性能