【刷题日记】leetcode-719. 找出第 K 小的数对距离

199 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

题目描述

image.png

题目释义

  • 数组nums,内容为正整数
  • 整数k,求数对距离升序排序,排序为k的的数对距离
  • 数对距离的算法|nums[n]-nums[m]|。

初看题最容易想到的是遍历所有数对,然后将距离放入一个有序数组中,然后取排序为k的值。 但是这样它的时间复杂度是O(N^2)。

寻找某一个数值,最常见的就是二分法。那这里应该以谁来进行二分呢?给定的k是一个数对距离的大小排序编号,所以这个二分对象应该是数组中存在的最大数对距离。因为是对数组内部的数据进行组合,所以借助双指针,来向右滑动实现数对结合。 假设数组nums的长度为n,则数组nums中所有对数的组合数是1+2+....+(n1)=n(n1)/2则数组nums中所有对数的组合数是1+2+....+(n-1)=n*(n-1)/2,而题目中给定的k的范围为1 <= k <= n * (n - 1) / 2,所以,排名为k的数对是一定存在的。

解题要点

  • 二分法+双指针 给定数组nums=[3,5,6,9,2,8],k=2 image.png 先将nums进行升序排序 image.png 排序结果nums=[2,3,5,6,8,9]

获取数对距离范围left=0,right=7, 则二分中点mid=left+(right-left)/2=3

image.png 寻找数对距离<3的对数

创建左指针i、右指针j,i=0;j=0;

用指针模拟数组内元素组合,创建循环,循环对象为j,循环条件为j<n。 右指针j依次向右移动,如果nums[j]-nums[i]>mid,说明此时左指针和j及j之后的所有元素无法组合成满足条件的数对,因此,左指针向右移一位。反之则说明从i位置到j位置的所有元素组合都满足数对距离值小于或等于mid,所以对满足条件的数对数量进行累加,即对j-i的值进行累加,值为count。 计算了当前满足mid条件的所有数对数量之后,对此值进行判断,如果count>k,说明mid值定得过大,满足的数对大于需求数,所以mid的取值范围应该向左二分,即right=mid-1;如果count<k,说明mid值定得过小,满足的数对小于需求数,所以mid的取值范围应该向左二分,即left=mid+1

再次进入获取所有满足条件的数对数方法,直到left>right

代码实现

public static int smallestDistancePair(int[] nums, int k) {
    //排序
    Arrays.sort(nums);
    int n = nums.length;
    //找到左右取值范围
    int left = 0, right = nums[n - 1] - nums[0];
    int ans = 0;
    //二分找第K小
    while (left <= right) {
        int mid = left + (right - left) / 2;
        int i = 0;
        int count = 0;
        //计算当前小于mid的数对有多少
        for (int j = 0; j < n; j++) {
            while (nums[j] - nums[i] > mid) {
                i++;
            }
            count += j - i;
        }
        //count小于则left = mid + 1,
        // 大于则right = mid - 1
        if (count >= k) {
            ans = mid;
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return ans;
}