[每日一题] leetcode 719. 找出第 K 小的数对距离

131 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

找出第 K 小的数对距离

数对(a,b)由整数ab组成,其数对距离定义为ab的绝对差值。数对 (a,b) 由整数 a 和 b 组成,其数对距离定义为 a 和 b 的绝对差值。

给你一个整数数组nums和一个整数k,数对由nums[i]nums[j]组成且满足0<=i<j<nums.length。返回所有数对距离中第k小的数对距离。给你一个整数数组 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

 

提示:
  • n == nums.length
  • 2 <= n <= 10^4
  • 0 <= nums[i] <= 10^6
  • 1 <= k <= n * (n - 1) / 2
思路:

要求的第k小的数对,那么面对这种题目,很显然,可以考虑二分的思路
对于所有数对,之间差的是一个绝对值。
那么我们自然而然的想到了这样一个思路
先排个序,去掉绝对值 为什么呢? 设 a < b 则 abs(a - b) = b - a
且对于每一个l的起点,其距离差是逐渐增加的
这个时候,我们就可以考虑是检查一下,当前小于距离x的点对有多少对了。
为什么可行呢, 因为第k小的数对距离一定有k-1个数对距离比他小,那么我们是不是可以逐步逼近这个距离
如何逼近这个距离呢? 二分是一个很好的方式,这个是二分答案考虑的经典问题
一部分是满足条件的,一部分是不满足条件的。 我们本题的check 就是去check他是不是有k个值小于他,若是有,则答案一定小于等于这个值
如此这样,即可解决本题。
用专业点(我给你换个字体)的话说,就是: 给定距离x,计算所有距离小于等于x的数对数目cnt可以使用二分查找:枚举所有数对的左端点i,二分查找小于等于nums[i]+x的最大值的下标i,那么右端点为j且距离小于等于nums[i]+x的数对数目为ji,计算这些数目之和。给定距离 x,计算所有距离小于等于 x 的数对数目 cnt 可以使用二分查找:枚举所有数对的左端点 i,二分查找小于等于 nums[i] + x 的最大值的下标 i,那么右端点为 j 且距离小于等于 nums[i] + x 的数对数目为 j - i,计算这些数目之和。
使用upper_bound来找到上届就比较方便
总体时间复杂度呢,就是一个 lognnlognlogn * n * logn的时间复杂度

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;
    }
};