给定一个整数数组,返回所有数对之间的第 k 个最小距离。一对 (A, B) 的距离被定义为 A 和 B 之间的绝对差值。
示例 1:
输入:
nums = [1,3,1]
k = 1
输出:0
解释:
所有数对如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
因此第 1 个最小距离的数对是 (1,1),它们之间的距离为 0。
提示:
2 <= len(nums) <= 10000.
0 <= nums[i] < 1000000.
1 <= k <= len(nums) * (len(nums) - 1) / 2.
解答
这里引用大神的分析:
设数组元素有n个,那么共有n*(n-1)/2个所谓的距离。这些距离的大小是有范围的,即最小的距离必大于等于0,最大的距离为数组的最大元素减去数组的最小元素。题目中所求的第k个最小距离就在其中。
我们可以用二分查找来找到这个目标距离,初始化l=0,r=最大距离。mid=(l+r)/2,计算出距离小于等于mid的数对的个数count。
若count<k,则第k个最小距离不会在[l,mid]之间,故修改l=mid+1;
否则,r=mid;(因为第k个最小距离在[l,mid]之间);
我自己写的具体算法如下:
public int smallestDistancePair(int[] nums, int k) {
Arrays.sort(nums);
int len = nums.length;
int low = 0 ,high = Math.abs(nums[len-1]-nums[0]) ,mid;
// 二分查找。第k个最小距离在[low,high]之间,在[low,mid],在[mid+1,high]...之间,
// 在[low(high),low(high)]之间,退出第k个最小距离就等于low
while (low < high){
mid = (low+high)/2;
// 计算nums中距离<=mid的对数(注意是小于等于)
int pairs = 0;
// 1.暴力法,最坏的情况下n(n-1)/2的时间复杂度
for (int i=0;i<len-1;++i){
for (int j=i+1;j<len;++j){
int val = Math.abs(nums[i]-nums[j]);
if (val > mid) break;
pairs++;
}
}
// 第k个最小距离必定在[mid+1,high]中,所有小于等于mid的距离对数pairs<k,那么pairs后面的距离都是大于mid
if (pairs < k){
low = mid + 1;
}else{
high = mid;
}
}
return low;
}
这里计算nums中距离<=mid的对数的时候,算法是穷举法,最坏的时间复杂度达到n(n-1)/2,这对整个算法的效率有很大的影响。关于这个算法后面有时间会分析。