题目

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

给你一个整数数组 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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-k-th-smallest-pair-distance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

首先,数组长度10^4级别,只能考虑nlogn的时间了。

可以发现,数组排序是不影响数对距离的计算的,而排序后可以考虑二分,可能的最小的距离为0,最大的距离为数组中最大值减去最小值。

二分数对距离mid,然后计算小于等于mid的数对个数cnt(此过程也是通过二分实现),和k作比较,如果cnt小于k,说明mid小了,调整二分下界,反之调整上界。见代码一。

其实上述找第k个xx的这个过程,有一点类似应用快排找数组中第k大元素。

代码

代码一 排序+二分

  1. class Solution {
  2. public int smallestDistancePair(int[] nums, int k) {
  3. Arrays.sort(nums);
  4. int n = nums.length;
  5. int low = 0;
  6. int high = nums[n - 1] - nums[0];
  7. while (low < high) {
  8. int mid = low + (high - low) / 2;
  9. // 统计距离不大于mid的数对个数
  10. int cnt = 0;
  11. for (int i = 0; i < n - 1; i++) {
  12. // 对于nums[i]来说,找到nums中最后一个不大于nums[i] + mid的元素,记其下标为j,那么不大于mid的数对个数为j-i
  13. cnt += bsearch(nums, nums[i] + mid) - i;
  14. }
  15. if (cnt < k) {
  16. low = mid + 1;
  17. } else {
  18. high = mid;
  19. }
  20. }
  21. return low;
  22. }
  23. // 查找nums中最后一个<=target的元素下标
  24. public int bsearch(int[] nums, int target) {
  25. int len = nums.length;
  26. int low = 0;
  27. int high = len - 1;
  28. while (low < high) {
  29. int mid = low + (high - low + 1) / 2;
  30. if (nums[mid] > target) {
  31. high = mid - 1;
  32. } else {
  33. low = mid;
  34. }
  35. }
  36. return low;
  37. }
  38. }

代码二 排序+二分+双指针

内部可以不使用二分,使用双指针就可以统计出距离不大于mid的数对。

枚举右端点,然后移动左端点,直到找到第一个距离小于等于mid的数,左右端点的距离即为该段的数对个数,然后继续移动右端点。

  1. class Solution {
  2. public int smallestDistancePair(int[] nums, int k) {
  3. Arrays.sort(nums);
  4. int n = nums.length;
  5. int low = 0;
  6. int high = nums[n - 1] - nums[0];
  7. while (low < high) {
  8. int mid = low + (high - low) / 2;
  9. int cnt = 0;
  10. for (int i = 0, j = 0; j < n; j++) {
  11. while (nums[j] - nums[i] > mid) {
  12. i++;
  13. }
  14. cnt += j - i;
  15. }
  16. if (cnt < k) {
  17. low = mid + 1;
  18. } else {
  19. high = mid;
  20. }
  21. }
  22. return low;
  23. }
  24. }