问题描述:
给定一个整数数组,返回所有数对之间的第 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
.
基本思路:
利用二分法的本质来做。
我们一开始先确定整数数组距离的上下界,取中间,与计数结果比对,然后二分。
不过如果不预处理的话还是会tle。
预处理的意义就在于我们使用较小的复杂度来代替二分携带的较高复杂度。
比如,不预处理,我们的二分复杂度可能是O(mlogn); 预处理,我们的复杂度则可以是O(m + logn) = O(logn);
由此可以看出预处理的重要性。
而且这道题用到的预处理思路十分简便且巧妙:
- 简便就简便在它只是在开头时使用了一次排序。
- 巧妙则在于二分时的统计,本来需要一次二重循环,但是它通过让内层循环不置零降低了复杂度。
具体地看代码把:
AC代码:
class Solution {
public:
int CountK(vector<int> &a, int k) {
int count = 0;
int left = 0;
for (int i = 1; i < a.size(); ++i) {
while (a[i] - a[left] > k) ++left;
count += (i - left);
}
return count;
}
int smallestDistancePair(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
int left = 0;
int right = nums[nums.size() - 1] - nums[0];
while (left < right) {
int mid = left + (right - left) / 2;
int count = CountK(nums, mid); // 寻找比mid还要小的差值数
if (count >= k) right = mid;
else left = mid + 1;
}
return left;
}
};