从何时你也忌讳空山无人,从何时开始|

Drunker•L

园龄:4个月粉丝:0关注:0

719. 找出第 K 小的数对距离

找出第 K 小的数对距离
数对 (a,b) 由整数 ab 组成,其数对距离定义为 ab 的绝对差值。

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

思路

  1. 排序

    • 首先对数组 nums 进行排序。因为在排序后的数组中,数对的距离有一个规律:|nums[i] - nums[j]|(其中 i < j)会随着 ij 的增大而增大。因此,排序后的数组有助于我们更高效地查找第 k 小的距离对。
  2. 二分查找

    • 问题是求第 k 小的距离,而距离的范围是从0nums[nums.length - 1] - nums[0](即排序后的数组中最大值和最小值的差)。

    • 我们可以使用二分查找来逼近第 k 小的距离。具体的二分查找目标是:

      • 左边界 left:表示最小的距离,即 0
      • 右边界 right:表示最大的距离,即 nums[nums.length - 1] - nums[0]
      • 通过判断中间的距离 mid,我们可以决定更新二分查找的左右边界,从而逐步找到第 k 小的距离。

      这里把 mid 这个中间距离挑出来的意思是因为,我想要通过统计小于等于 mid 这个中间距离的数对的个数来决定,我这个二分的区间应该怎样去收缩。并且也说明我要写一个用于统计数量的函数,也就是下面的 countPairs

  3. 计数函数

    • 对于每一个 mid,我们需要计算所有数对 (nums[i], nums[j]),使得 |nums[i] - nums[j]| <= mid 的数对的数量。我们使用双指针技巧来高效计算这个数量。
  • 使用指针 i 固定一个元素,指针j 作为第二个元素,ji+1 开始向右移动,直到 nums[j] - nums[i] > mid
  • 在此过程中,i 固定时,所有符合 nums[j] - nums[i] <= mid 条件的数对 (i, j) 都是有效的。
  • countPairs(mid) 函数的时间复杂度是 O(n),因为我们只需要一次遍历数组即可。
  1. 如何更新二分查找的左右边界
    • 如果对于当前的 midcountPairs(mid) 的值小于 k,说明当前的 mid 太小,我们需要增加 mid,因此更新左边界 left = mid + 1
    • 如果 countPairs(mid) 的值大于或等于 k,说明当前的 mid 可能是解或者可以继续缩小,所以更新右边界 right = mid
import java.util.Arrays;

class Solution {
    public int smallestDistancePair(int[] nums, int k) {
        Arrays.sort(nums); // 排序数组
        int left = 0, right = nums[nums.length - 1] - nums[0]; // 左右边界初始化
        
        while (left < right) {
            int mid = left + (right - left) / 2; // 计算 mid,避免溢出
            if (countPairs(nums, mid) < k) {
                left = mid + 1; // 如果符合条件的数对小于 k,更新左边界
            } else {
                right = mid; // 否则更新右边界
            }
        }
        return left; // 最终的 left 即为第 k 小的距离
    }

    // 计算所有 |nums[i] - nums[j]| <= mid 的数对个数
    private int countPairs(int[] nums, int mid) {
        int count = 0;
        int j = 0;
        
        // 使用双指针技巧
        for (int i = 0; i < nums.length; i++) {
            // 向右移动 j,直到 nums[j] - nums[i] > mid
            while (j < nums.length && nums[j] - nums[i] <= mid) {
                j++;
            }
            // 计算符合条件的数对个数,减去 i 和 i 的情况
            count += (j - i - 1);
        }
        return count;
    }
}

本文作者:Drunker•L

本文链接:https://www.cnblogs.com/drunkerl/p/18664658

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Drunker•L  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起