1.题目描述
数对 (a,b) 由整数 a 和 b 组成,其数对距离定义为 a 和 b 的绝对差值。
给你一个整数数组 nums 和一个整数 k ,数对由 nums[i] 和 nums[j] 组成且满足 0 <= i < j < nums.length 。返回 所有数对距离中 第 k 小的数对距离。
示例 1:
输入:nums = [1,3,1], k = 1
输出:0
解释:数对和对应的距离如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
距离第 1 小的数对是 (1,1) ,距离为 0 。
示例 2:
输入:nums = [1,1,1], k = 2
输出:0
示例 3:
输入:nums = [1,6,1], k = 3
输出:5
2.解题思路与代码
2.1 解题思路
题目要求数组中两两数字之间第 k 小的数对距离,需求就是两个:计算数对距离、找到第 k 小个距离。那么我们就根据题目分析,数对距离的范围是在从 0 到数组最大值减数组最小值,即 [0, max(nums)-min(nums)] 这个范围上。那么我们就可以转换一下思路,将原题改成在 [0, max(nums)-min(nums)] 这个范围上寻找数组中的第 k 小数对距离。
思考到这一步之后这道题就容易多了,我们首先对数组按照从小到大进行排序,主体使用二分查找进行,在每一次计算 mid 之后,在数组中计算小于等于 mid 的数堆个数,如果大于 k ,则向左半区域继续查找;如果小于等于 k,则向右半区域继续查找,直到 left 等于 right ,最后返回 left 或 right 。
以题目示例 3 为例再对过程进行分析:
数组为 [1, 6, 1],k 为 3,此时对数组排序后数组变为 [1, 1, 6],那么我们的查找范围就是 [0, 5]
2.2 代码
class Solution {
public int smallestDistancePair(int[] nums, int k) {
Arrays.sort(nums);
int left = 0;
int right = nums[nums.length - 1] - nums[0];
while (left < right) {
int mid = (left + right) / 2;
int countPairs = countPairs(nums, mid);
if (countPairs < k) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
public int countPairs(int[] nums, int mid) {
int l = 0;
int r = 0;
int count = 0;
while (r < nums.length) {
while (l < r && nums[r] - nums[l] > mid) {
l++;
}
count += r - l;
r++;
}
return count;
}
}
2.3 测试结果
通过测试
3.总结
- 使用二分查找法在 [0, max(nums)-min(nums)] 范围上查找
- 每次计算完 mid 便统计数组中小于等于 mid 的数对个数,如果大于 k ,则向左半区域继续查找;如果小于等于 k,则向右半区域继续查找