携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
找出第 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
提示:
n == nums.length2 <= n <= 10^40 <= nums[i] <= 10^61 <= k <= n * (n - 1) / 2
思路:
要求的第k小的数对,那么面对这种题目,很显然,可以考虑二分的思路
对于所有数对,之间差的是一个绝对值。
那么我们自然而然的想到了这样一个思路
先排个序,去掉绝对值 为什么呢? 设 a < b 则 abs(a - b) = b - a
且对于每一个l的起点,其距离差是逐渐增加的
这个时候,我们就可以考虑是检查一下,当前小于距离x的点对有多少对了。
为什么可行呢, 因为第k小的数对距离一定有k-1个数对距离比他小,那么我们是不是可以逐步逼近这个距离
如何逼近这个距离呢? 二分是一个很好的方式,这个是二分答案考虑的经典问题
一部分是满足条件的,一部分是不满足条件的。 我们本题的check 就是去check他是不是有k个值小于他,若是有,则答案一定小于等于这个值
如此这样,即可解决本题。
用专业点(我给你换个字体)的话说,就是:
使用upper_bound来找到上届就比较方便
总体时间复杂度呢,就是一个 的时间复杂度
class Solution {
public:
int smallestDistancePair(vector<int>& nums, int k) {
int res = 0, n = nums.size();
sort(nums.begin(), nums.end());
int l = 0, r = nums.back() - nums[0];
while (l < r) {
int x = l + r >> 1;
int cnt = 0;
for (int i = 0; i + 1 < n; i ++) {
auto t =
upper_bound(nums.begin() + i, nums.end(), nums[i] + x);
t --;
cnt += t - nums.begin() - i;
}
if (cnt < k) l = x + 1;
else r = x;
}
return l;
}
};