leetcode 719. Find K-th Smallest Pair Distance(找到第k小的距离)

Given an integer array, return the k-th smallest distance among all the pairs. The distance of a pair (A, B) is defined as the absolute difference between A and B.

Example 1:
Input:
nums = [1,3,1]
k = 1
Output: 0
Explanation:
Here are all the pairs:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
Then the 1st smallest distance pair is (1,1), and its distance is 0.

Note:
2 <= len(nums) <= 10000.
0 <= nums[i] < 1000000.
1 <= k <= len(nums) * (len(nums) - 1) / 2.

给一个数组,找到数组中所有元素对之间第k小的距离

思路:
1) brute force
计算所有pair的距离,然后用桶排序统计每个距离出现的次数,找到第k个距离。
桶排序用的数组size到数组中的最大值,作为最大值-0的距离。

//593ms
    public int smallestDistancePair(int[] nums, int k) {
        int maxNum = 0;
        int n = nums.length;
        int result = 0;
        int count = 0;
        for(int i = 0; i < n; i++) {
            if(nums[i] > maxNum) maxNum = nums[i];
        }
        int[] dis = new int[maxNum+1];
        
        for(int i = 0; i < n-1; i ++) {
            for(int j = i+1; j < n; j++) {
                dis[Math.abs(nums[j] - nums[i])] ++;
            }
        }
        //dis.length:maxNum
        for(int i = 0; i <= maxNum; i++) {
            count += dis[i];
            if(count >= k) {
                result = i;
                break;
            }
        }
        return result;
    }

2)binary search + DP
把问题转化一下
问题是要找到第k小的距离,转化为<=某距离的pair有k个
那么只需找到这个“某距离”即可

定义某距离为m,遍历数组,找到每个元素到其他元素的距离<=m的个数,定义dp[i]为:到index=i为止的元素,也就是nums[0] ~ nums[i]中每个元素到其它元素pair distance<=m的累加和。即dp[i] = dp[i-1] + nums[i]到其它元素pair distance<=m的个数。

下面就是如何计算nums[i]到其它元素pair distance<=m的个数。
先把数组升序排序,可以保证只计算nums[i]右边元素到nums[i]的距离。
找到第一个nums[j]使nums[j] - nums[i] > m,这样( nums[i] , nums[j] )区间的数字到nums[i]的距离就会<=m, 区间中数字的个数是(j - i - 1)
所以dp[i] = dp[i-1] + (j - i - 1)

当遍历完数组,累加了所有pair distance <= m的个数后,如果个数>k,说明m偏大,满足条件的个数过多,要把右边界调整到m-1。反之左边界调整到m+1。做binary search,直到找到合适的m使得个数==k。

因为dp每次只用到前面一个元素的信息,所以dp可以不用数组,降维到变量。

//4ms
    public int smallestDistancePair(int[] nums, int k) {
        Arrays.sort(nums);
        int n = nums.length;
        int left = 0;
        int right = nums[n-1] - nums[0];  //最大distance
        
        while(left <= right) {
            int dp = 0;
            int mid = left + (right - left) / 2;   
            int j = 0;
            //找到nums[j] - nums[i] > mid, j是递增的,不用从0开始
            for(int i = 0; i < n-1; i++) {
                while(j < n && nums[j] - nums[i] <= mid) j++;
                dp += (j-i-1); //dp[i] = dp[i-1]+(j-i-1)
            }
            
            //distance太大,满足条件的很多,缩小right
            if(dp >= k) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

参考资料

你可能感兴趣的:(leetcode,算法,leetcode)