题目来源:https://leetcode.cn/problems/find-k-th-smallest-pair-distance/
大致题意:
给一个数组 nums,定义数对距离为 nums[j] - nums[i] 的绝对值,找出数组所有数对中第 k 小的数对距离
思路
- 对数组排序,那么数对距离的取值范围为 [ 0, nums[n - 1] - nums[0] ]
- 于是在数对距离范围中二分搜索第 k 小的数对距离
对于当前数对距离 mid,枚举求出小于等于 mid 的数对个数 count,若 k > count,表示当前二分中值小于目标值,更新左边界为 mid + 1;反之,表示当前二分中值大于等于目标值,更新右边界为 mid - 1
枚举时可以使用双指针优化,初始时两个指针都为 0,右指针逐步增加,统计以当前右指针为右边界的数对差小于mid的数对个数。
在左指针小于右指针且当前右指针处元素减去左指针处元素的数对差大于 mid时,更新左指针。
- 最后二分搜索的左边界值即为目标值
二分过程中,枚举到目标值时会更新右边界,让右边界的值小于目标值,也就是说此时二分边界内的值都小于目标值,在之后的枚举过程中会一直更新左边界,直至左边界大于右边界,这时左边界的值也就是目标值了
代码:
public int smallestDistancePair(int[] nums, int k) {
// 排序
Arrays.sort(nums);
int n = nums.length;
// 初始化二分边界
int l = 0;
int r = nums[n - 1] - nums[0];
// 二分
while (l <= r) {
int mid = (l + r) >> 1;
// 初始化双指针
int idx1 = 0;
int idx2 = 0;
// 差小于等于当前二分中值的数对数目
int count = 0;
while (idx2 < n) {
// 若当前数对差大于二分中值,左边界右移
while (idx1 < idx2 && nums[idx2] - nums[idx1] > mid) {
idx1++;
}
// idx2 - idx1 即为以 idx2 为右边界的数对差小于二分中值的数对个数
count += idx2 - idx1;
// 更新右边界
idx2++;
}
// 若 k 大于 所有数对中小于当前二分中值的数对数目,表示当前二分中值小于目标值,更新左边界
if (k > count) {
l = mid + 1;
} else { // 否则,表示当前二分中值大于等于目标值,更新右边界
r = mid - 1;
}
}
/*
二分过程中,枚举到目标值时会更新右边界,让右边界的值小于目标值,也就是说
此时二分边界内的值都小于目标值,在之后的枚举过程中会一直更新左边界,直至左边界大于右边界
这时左边界的值也就是目标值了
*/
return l;
}