Given an integer array, return the k-th smallest distance among all the pairs. The distance of a pair (A, B) is defined as the absolute difference between A and B.
Example 1:
Input:
nums = [1,3,1]
k = 1
Output: 0
Explanation:
Here are all the pairs:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
Then the 1st smallest distance pair is (1,1), and its distance is 0.
Note:
2 <= len(nums) <= 10000.
0 <= nums[i] < 1000000.
1 <= k <= len(nums) * (len(nums) - 1) / 2.
给一个数组,找到数组中所有元素对之间第k小的距离
思路:
1) brute force
计算所有pair的距离,然后用桶排序统计每个距离出现的次数,找到第k个距离。
桶排序用的数组size到数组中的最大值,作为最大值-0的距离。
//593ms
public int smallestDistancePair(int[] nums, int k) {
int maxNum = 0;
int n = nums.length;
int result = 0;
int count = 0;
for(int i = 0; i < n; i++) {
if(nums[i] > maxNum) maxNum = nums[i];
}
int[] dis = new int[maxNum+1];
for(int i = 0; i < n-1; i ++) {
for(int j = i+1; j < n; j++) {
dis[Math.abs(nums[j] - nums[i])] ++;
}
}
//dis.length:maxNum
for(int i = 0; i <= maxNum; i++) {
count += dis[i];
if(count >= k) {
result = i;
break;
}
}
return result;
}
2)binary search + DP
把问题转化一下
问题是要找到第k小的距离,转化为<=某距离的pair有k个
那么只需找到这个“某距离”即可
定义某距离为m,遍历数组,找到每个元素到其他元素的距离<=m的个数,定义dp[i]为:到index=i为止的元素,也就是nums[0] ~ nums[i]中每个元素到其它元素pair distance<=m的累加和。即dp[i] = dp[i-1] + nums[i]到其它元素pair distance<=m的个数。
下面就是如何计算nums[i]到其它元素pair distance<=m的个数。
先把数组升序排序,可以保证只计算nums[i]右边元素到nums[i]的距离。
找到第一个nums[j]使nums[j] - nums[i] > m,这样( nums[i] , nums[j] )区间的数字到nums[i]的距离就会<=m, 区间中数字的个数是(j - i - 1)
所以dp[i] = dp[i-1] + (j - i - 1)
当遍历完数组,累加了所有pair distance <= m的个数后,如果个数>k,说明m偏大,满足条件的个数过多,要把右边界调整到m-1。反之左边界调整到m+1。做binary search,直到找到合适的m使得个数==k。
因为dp每次只用到前面一个元素的信息,所以dp可以不用数组,降维到变量。
//4ms
public int smallestDistancePair(int[] nums, int k) {
Arrays.sort(nums);
int n = nums.length;
int left = 0;
int right = nums[n-1] - nums[0]; //最大distance
while(left <= right) {
int dp = 0;
int mid = left + (right - left) / 2;
int j = 0;
//找到nums[j] - nums[i] > mid, j是递增的,不用从0开始
for(int i = 0; i < n-1; i++) {
while(j < n && nums[j] - nums[i] <= mid) j++;
dp += (j-i-1); //dp[i] = dp[i-1]+(j-i-1)
}
//distance太大,满足条件的很多,缩小right
if(dp >= k) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}