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());
}
}