每日一道leetcode(2026.04.16):距离最小相等元素查询

0 阅读3分钟

1. 题目

给你一个 环形 数组 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 <= 10e5 1 <= nums[i] <= 10e6 0 <= queries[i] < nums.length

2. 分析

这道题能看到很多其它题型的影子,其实就是快速找到指定下标位置元素值最相近的元素位置。这里我一开始是通过一个数组nums的全遍历,将所有出现的数值放到一个Map中,key为对应的数值,value为出现的所有下标List,这样再确认最小距离的时候,只需要找到指定的下标,然后比较下标前一个和后一个的距离,取较小值即可。

需要稍微注意的是,nums数组表述为环形数组,即下标0和length-1是相连的。

按照这个思路实现逻辑后,通过了绝大多数的测试用例,但倒在了下面这个精心定制的用例上。

在这里插入图片描述

在这个用例中,数组nums由特别多是1组成,queries又是分布特别广泛的大数,这样在定位对应的下标时就会执行特别多次循环。

出于这种相同Key值对应List长度超长的情况,我试着使用双向指针的方式去获取最短的距离,所以就基于数组长度以64为间隔,小的范畴仍沿用List中的遍历,大的范畴使用双向指针。

3. 代码实现

class Solution {
    public List<Integer> solveQueries(int[] nums, int[] queries) {
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])) {
                map.put(nums[i], new ArrayList<>());
            }
            List<Integer> list = map.get(nums[i]);
            if (list.size() > 64) {
                continue;
            }
            list.add(i);
        }
        int[] res = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            int index = queries[i];
            int query = nums[index];
            List<Integer> list = map.get(query);
            if (list == null || list.size() <= 1) {
                res[i] = -1;
                continue;
            }

            if (list.size() > 64) {
                // 使用双向指针进行遍历
                int left = index;
                int right = index;
                int count = 0;
                while (true) {
                    left--;
                    count++;
                    if (left < 0) {
                        left = nums.length - 1;
                    }
                    if (nums[left] == query) {
                        res[i] = count;
                        break;
                    }
                    right++;
                    if (right > nums.length - 1) {
                        right = 0;
                    }
                    if (nums[right] == query) {
                        res[i] = count;
                        break;
                    }
                }
                continue;
            }
            // 使用记录的数组进行遍历
            for (int j = 0; j < list.size(); j++) {
                int val = list.get(j);
                if (val == index) {
                    // 当前查询的位置,比较左边和右边的距离取较小值
                    int left = j == 0 ? val + nums.length - list.get(list.size() - 1) : val - list.get(j - 1);
                    int right = j == list.size() - 1 ? nums.length - val + list.get(0) : list.get(j + 1) - val;
                    res[i] = Math.min(left, right);
                    break;
                }
            }
        }
        return IntStream.of(res).boxed().collect(Collectors.toList());
    }
}

在这里插入图片描述