leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 41 】2021-10-20 - 822. Kth-Pair-Distance #58

Open azl397985856 opened 3 years ago

azl397985856 commented 3 years ago

822. Kth-Pair-Distance

入选理由

暂无

题目地址

https://binarysearch.com/problems/Kth-Pair-Distance

前置知识

Constraints

n ≤ 100,000 where n is the length of nums Example 1 Input nums = [1, 5, 3, 2] k = 3 Output 2 Explanation Here are all the pair distances:

abs(1 - 5) = 4 abs(1 - 3) = 2 abs(1 - 2) = 1 abs(5 - 3) = 2 abs(5 - 2) = 3 abs(3 - 2) = 1 Sorted in ascending order we have [1, 1, 2, 2, 3, 4].

itsjacob commented 3 years ago

Intuition

Implementation

class Solution
{
public:
  int smallestDistancePair(vector<int> &nums, int k)
  {
    // The search space for the pair distance is [0, max{nums} - min{nums}]
    // the helper subroutine takes in a distance parameter and returns the number of pairs that is <= that parameter
    // the array needs to be sorted
    std::sort(nums.begin(), nums.end());
    int left{ 0 };
    int right = nums.back() - nums.front();
    while (left <= right) {
      int mid = left + (right - left) / 2;
      int nPairsBeforeMid = countDistancePairs(nums, mid);
      if (nPairsBeforeMid == k) {
        // Too many pairs, shrink the right side
        right = mid - 1;
      } else if (nPairsBeforeMid > k) {
        right = mid - 1;
      } else if (nPairsBeforeMid < k) {
        left = mid + 1;
      }
    }
    return left;
  }

private:
  /*
   * @return the number of distance pairs that is <= upperBound
   */
  int countDistancePairs(vector<int> &nums, int upperBound)
  {
    // two-pointer sliding window problem
    int fast{ 0 };
    int slow{ 0 };
    int res{ 0 };
    while (fast < nums.size()) {
      // fast sets the right boundary of the window
      // shrink the window if needed
      // the next j doesn't have to restart the slow pointer from 0
      // because if nums[j] - nums[slow] > upperBound, then nums[j+1] - nums[i] > upperBound for i <= slow
      while (nums[fast] - nums[slow] > upperBound) slow++;
      res += fast - slow;
      fast++;
    }
    return res;
  }
};

Complexity

ginnydyy commented 3 years ago

Problem

https://binarysearch.com/problems/Kth-Pair-Distance

Notes

Solution

import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        Arrays.sort(nums);
        int start = 0;
        int end = nums[nums.length - 1] - nums[0];
        while(start + 1 < end){
            int mid = start + (end - start) / 2;
            if(count(nums, mid) < k){
                start = mid;
            }else if(count(nums, mid) > k){
                end = mid;
            }else{
                start = mid;// if there are k pairs' diff <= mid, mid may be the k - 1th smallest, need to try whether there is a bigger mid 
            }
        }
        return count(nums, start) <= k? end: start; // if there are k pairs' diff <= start, start may be the k - 1th smallest, then end is the kth smallest
    }

    // count how many pairs' diff is <= mid
    private long count(int[] nums, int mid){
        long result = 0; // int would cause overflow, because n <= 100,000, result is greater than max int
        int left = 0;
        for(int right = 1; right < nums.length; right++){
            while(nums[right] - nums[left] > mid){
                left++;
            }
            if(nums[right] - nums[left] <= mid){
                result += right - left;
            }
        }
        return result;
    }
}

Complexity

carsonlin9996 commented 3 years ago

思路

二分法找可行的diff, 且该diff的对数是少于k 双指针寻找对应diff的pair数量

最后判断leftdiff 的数量必须大于等于k(多对pair有相同的leftdiff), 否则return rightdiff

class Solution {
    public int smallestDistancePair(int[] nums, int k) {
        Arrays.sort(nums);

        int left = 0;
        int right = nums[nums.length - 1] - nums[0];

        while (left + 1 < right) {
            int midDiff = left + (right - left) / 2;

            if (getDiffCnt(midDiff, nums) >= k) {
                right = midDiff;
            } else {
                left = midDiff;
            }
        }
        //判断leftDiff的数量是大于等于第k对
        if (getDiffCnt(left, nums) >= k) {
            return left;
        }
        return right;
    }

    private int getDiffCnt(int diff, int[] nums) {

        int i = 0;
        int cnt = 0;
        int j = 1;

        while (j < nums.length) {
            while (nums[j] - nums[i] > diff) {
                i++;
            }
            cnt += j - i;
            j++;
        }
        return cnt;
    }
}

Time/Space : O(NlogN), O(1)

learning-go123 commented 3 years ago

思路

代码

Go Code:


func getCount(nums []int, mid int) int {
    var res int
    for i := 0; i < len(nums); i++ {
        j := 0
        for nums[i]-nums[j] > mid {
            j++
        }
        res += i - j
    }
    return res
}

func solve(nums []int, k int) int {
    sort.Ints(nums)
    left, right := 0, nums[len(nums)-1]-nums[0]
    for left <= right {
        mid := (right-left)/2 + left
        if getCount(nums, mid) < k {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }
    return left
}

复杂度分析

令 n 为数组长度。

zhangyalei1026 commented 3 years ago

思路

代码

class Solution:
    def solve(self, A, k):
        A.sort()
        def count_not_greater(diff):
            i = ans = 0
            for j in range(1, len(A)):
                while A[j] - A[i] > diff:
                    i += 1
                ans += j - i
            return ans
        l, r = 0, A[-1] - A[0]
        k += 1 
        while l <= r:
            mid = (l + r) // 2
            if count_not_greater(mid) >= k:
                r = mid - 1
            else:
                l = mid + 1
        return l
HondryTravis commented 3 years ago

思路

参考题解思路

代码

class Solution {
    solve(nums, k) {
        nums = nums.sort((a, b) => a - b)

        let left = 0, right = nums[nums.length - 1] - nums[0], mid

        while (left <= right) {
            mid = left + ((right - left) >>> 1)

            if (this.ok(nums, mid) > k) right = mid - 1
            else left = mid + 1
        }
        return left
    }
    ok(nums, mid) {
        let res = 0, i = 0

        for (let j = 1; j < nums.length; j++) {
            while ((nums[j] - nums[i]) > mid) i ++
            res += j - i
        }
        return res
    }

}

复杂度分析

时间复杂度:O(logN)

空间复杂度:O(1)

ysy0707 commented 3 years ago

思路:二分 排序

class Solution {
    public int solve(int[] nums, int k) {
        Arrays.sort(nums);
        int n = nums.length;
        int left = 0, right = nums[n - 1] - nums[0];

        while(left <= right){
            int mid = left + (right - left) / 2;
            int cnt = 0,start = 0;
            for(int i = 0; i < n; ++i){
                while(start < n && nums[i] - nums[start] < mid) ++start;
                cnt += i - start;
            }
            if(cnt < k){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return left; 
    }
}

时间复杂度:O(NlogN) 空间复杂度:O(1)

JK1452470209 commented 3 years ago

思路

暴力枚举所有解,然后排序找出第k个(时间超时

代码

public Integer Distance_(int[] ints,int k){
        List<Integer> ret = new ArrayList<>();
        for (int i = 0; i < ints.length; i++) {
            for (int j = i + 1; j < ints.length; j++) {
                ret.add(Math.abs(ints[i] - ints[j]));
            }
        }
        ret.sort(Integer::compareTo);
        return ret.get(k - 1);
    }

复杂度

时间复杂度:O(N^2)

空间复杂度:O(N)

思路

二分+滑动窗口 copy大佬的

代码

public int solve(int[] nums, int k) {
        Arrays.sort(nums);
        int absMin = 0;
        int absMax = nums[nums.length-1] - nums[0];

        while (absMin <= absMax) { 
            int absMid = (absMin + absMax) / 2;
            System.out.println("mid: " + absMid);
            if (count(nums, absMid) <= k) {
                absMin = absMid + 1;
            } else {
                absMax = absMid - 1;
            }
        }

        return absMin;
    }

    private long count(int[] nums, int targetDiff) {
        long counter = 0;
        int l = 0;
        for (int r=1; r<nums.length; r++) {
            while (nums[r] - nums[l] > targetDiff) {
                l++;
            }

            counter += r - l;
        }
        return counter;
    }

复杂度

时间复杂度:O(NlogN)

空间复杂度:O(1)

leolisgit commented 3 years ago

思路

对值进行二分。对于每一个mid值,遍历数组,计算有多少个pair的绝对值<=mid.

代码

class Solution {
    public int solve(int[] nums, int k) {
        Arrays.sort(nums);

        int start = 0;
        int end = nums[nums.length - 1] - nums[0];
        k += 1;
        while (start <= end) {
            int mid = start + (end - start) / 2;
            if (countNoGreaterThan(nums, mid) >= (long) k) {
                end = mid - 1;
            } else {
                start = mid + 1; 
            }
        }
        return start;
    }

    public long countNoGreaterThan(int[] nums, int x) {
        long count = 0;
        int i = 0;
        for (int j = 1; j < nums.length; j++) {
            while (nums[j] - nums[i] > x) {
                i++;
            }
            count += j - i;
        }
        return count;
    }
}

复杂度

时间:O(nlogn)
空间:O(1)

Menglin-l commented 3 years ago

思路:

看到第k小,第k大的直觉用堆,然后发现超时了。。。

换个想法,给数组排序,排序后,最小距离为0,最大距离为nums[nums.length - 1] - nums[0]。再用二分,找出符合条件的距离对。


代码部分:

class Solution {

    public int smallestDistancePair(int[] nums, int k) {

        Arrays.sort(nums);
        int len = nums.length;
        int left = 0;
        int right = nums[len - 1] - nums[0];
        while (left <= right) {
            int mid = (left + right) / 2;
            if (isCounted(nums, mid) < k) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }

    public int isCounted(int[] nums, int distance) {
        int l = 0;
        int count = 0;
        for (int r = 0; r < nums.length; r ++) {
            while (nums[r] - nums[l] > distance) {
                l ++;
            }
            count += r - l;
        }
        return count;
    }
}

复杂度:

Time: O(NlogN)

Space: O(1)

florenzliu commented 3 years ago

Explanation

Code

class Solution:
    def solve(self, nums, k):
        nums.sort()
        def helper(target):
            count = 0
            prev = 0
            for i in range(1, len(nums)):
                while nums[i] - nums[prev] > target:
                    prev += 1
                count += i - prev
            return count

        start, end = 0, nums[-1] - nums[0]
        while start <= end:
            mid = start + (end - start) // 2
            if helper(mid) > k:
                end = mid - 1
            else:
                start = mid + 1
        return start

Complexity

ccslience commented 3 years ago
long long count(vector<int>& nums, int d)
{
    int i = 0;
    long long res = 0;
    for(int j = 1; j < nums.size(); j++)
    {
        while((nums[j] - nums[i]) > d)
            i++;
        res += (j - i);
    }
    return res;
}

int solve(vector<int>& nums, int k)
{
    sort(nums.begin(), nums.end());
    int l = 0, r = nums[nums.size() - 1] - nums[0];
    k++;
    while(l <= r)
    {
        int mid = l + (r - l ) / 2;
        if (count(nums, mid) >= k)
            r = mid - 1;
        else
            l = mid + 1;
    }
    return l;
}
Huangxuang commented 3 years ago

题目:Kth Pair Distance

思路

代码

class Solution:
    def solve(self, A, k):
        A.sort()
        def count_not_greater(diff):
            i = ans = 0
            for j in range(1, len(A)):
                while A[j] - A[i] > diff:
                    i += 1
                ans += j - i
            return ans
        l, r = 0, A[-1] - A[0]
        k += 1 # zero based -> one based
        while l <= r:
            mid = (l + r) // 2
            if count_not_greater(mid) >= k:
                r = mid - 1
            else:
                l = mid + 1
        return l

复杂度分析

Moin-Jer commented 3 years ago

思路


二分取值空间

代码


import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        Arrays.sort(nums);
        int len = nums.length;
        int l = 0, r = nums[len - 1] - nums[0];
        k += 1;
        while (l <= r) {
            int mid = (l + r) >>> 1;
            if (count(nums, mid) >= k) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return l;
    }

    private long count(int[] nums, int mid) {
        long ans = 0;
        int i = 0;
        for (int j = 1; j < nums.length; j++) {
            while (nums[j] - nums[i] > mid) {
                i++;
            }
            ans += j -i;
        }
        return ans;
    }
}

复杂度分析


ZJP1483469269 commented 3 years ago

思路

二分

代码

import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        k = k+1;
        Arrays.sort(nums);
        int l = 0,r = nums[nums.length-1]-nums[0];
        while(l<=r){
            int mid = l + (r - l) / 2;
            if(isok(mid,nums) >= k) r = mid - 1;
            else l = mid + 1; 
        }
        return l;

    }
    public long isok(int x, int[] nums){
        long ans = 0;
        int i=0;
        for(int j =1;j<nums.length;j++){
            while(nums[j]-nums[i] > x){
                i++;
            }
            ans +=(j-i);
        }

        return ans;
    }
}

复杂度分析:

时间复杂度:O(nlogn) 空间复杂度:O(1)

HouHao1998 commented 3 years ago

思路

统计个数,需要排序,用k比较个数大小

代码

    class Solution {
        public int KthPairDistance(int[] nums,int k) {
            k++;
            Arrays.sort(nums);
            int l= 0,r= nums[nums.length-1]-nums[0],ans= 0;
            while (l<=r){
                int mid= (r+l)/2;
                if(distance(nums,k,mid)){
                    l = mid+1;
                }else {
                    ans=mid;
                    r = mid-1;
                }
            }
            return ans;
        }
        public boolean  distance(int[] nums,int k,int mid) {
            int sum = 0;
            for (int i = 0; i < nums.length; i++) {
                for (int j = i+1; j < nums.length; j++) {
                    if(nums[j]-nums[i]<=mid){
                        sum++;
                    }else {
                        break;
                    }
                }
            }
            return k > sum;
        }
    }

复杂度

时间复杂度:O(nlogn) 空间复杂度:O(1)

chen445 commented 3 years ago

代码

 def smallestDistancePair(self, nums: List[int], k: int) -> int:
        nums.sort()
        l, r = 0, nums[-1] - nums[0]
        def count_pairs(mid):
            count = left = 0
            for i in range(len(nums)):
                while nums[i] - nums[left] > mid:
                    left += 1
                count += i - left
            return count >= k
        while l < r:
            mid = (l + r)//2
            if count_pairs(mid):
                r= mid
            else:
                l = mid + 1
        return l

复杂度

Time: O(nlogn)

Space: O(1)

carterrr commented 3 years ago
import java.util.*;

class Solution {
    public int solve(int[] nums, int k) {
        k += 1; 
        Arrays.sort(nums);
        int l = 0, r = nums[nums.length - 1] - nums[0];
        while( l < r) {
            int mid = (l + r) >> 1;
            //   看差值mid 之前有多少更小的差值数字  加起看是否大于等于k 如果大于等于k了 说明mid取大了
            boolean ok = isK(mid, k, nums);
            // find a value 
            if(ok){
                r = mid;  
            } else {
                l = mid + 1;
            }
        }
        return l;
    }

    private boolean moreThanOrEqualsK(int mid, int k, int[] numsSorted) {
        int pairsDiffGreaterThanKCnt = 0;
        for(int l = 0, r = 0; l < numsSorted.length; l++) {
            // 求差值要小于等于 mid的有多少对数字  如 1 2 3 4 第一小的是1 
            // 差值  4, 3, 2,2, 1 中小于等于3的有4个 重复的也算
            while((r < numsSorted.length) && (numsSorted[r] - numsSorted[l]) <= mid) {r ++;}; 
            if( (pairsDiffGreaterThanKCnt += (r-l-1) ) >= k) return true;

        }
        return false;
    }
}
Venchyluo commented 3 years ago

暴力解N**2 会超时。
二分其实就是找到一个mid,然后去排序好的队列里找到pair(a,b) b-a <= target, 那么[a,b] 里每一个点都可以组成合理的pair。 但是这里具体哪几个数字组成pair 不重要,只需要一个count 来计算 每取一个mid,这个数组中有多少对 小于等于 mid, 然后用count再跟k比较判断是left/right变动。

class Solution:
    def solve(self, nums, k):
        nums.sort()        
        left,right = 0, nums[-1]
        while left < right:
            mid = left + (right-left)//2
            if self.valid(nums,mid) > k:
                right= mid 
            else:
                left = mid +1
        return left

    def valid(self,nums,target):
        #   求出比pair(a,b)比mid小的值有多少个 => 每一个点,找到后面的一个点pair(a,b) <= target: 说明[a,b] 之间每一个点都可以凑成一个pair
        count = 0
        l = 0
        # 类似双指针思想来计算count
        for r in range(len(nums)):
            while nums[r] - nums[l] > target:
               l += 1
            count += r - l
        return count

Time complexity: O(N logN) Space complexity: O(1)

asterqian commented 3 years ago

思路

二分,通过一个helper来计算given difference,有多少pair的difference小于等于这个值。如果对数大于等于k,那么我们可以缩小范围到左边(移动r),因为超过了需要的范围,如果对数小于k,我们移动l往右边压缩范围。

代码

// helper method that returns the number of absolute values(pair) <= n
long long helper(vector<int>& nums, int n) {
    long long ans = 0;
    int i = 0;
    // 2 pointers
    for (int j = 1; j < nums.size(); ++j) {
        // diff > n means that we need to discard this pair and want to look for smaller ones
        while (nums[j] - nums[i] > n) {
            i++;
        }
        ans += (j - i);
    }
    return ans;
}

// find a difference x, making k absolute values smaller/equal to the number of x 
int solve(vector<int>& nums, int k) {
    sort(nums.begin(), nums.end());
    int l = 0, r = nums.back() - nums[0]; // answer exists in [0, diff(max of nums, min of nums)]
    k++; // turn into 0-indexed

    while (l <= r) {
        int mid = l + (r - l) / 2;
        if (helper(nums, mid) >= k) {
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    return l;
}

Time Complexity: O(NlogN)

Space Complexity: O(1)

cy-sues commented 3 years ago

Mark住

Cartie-ZhouMo commented 3 years ago
class Solution:
    def solve(self, A, k):
        A.sort()
        def count_not_greater(diff):
            i = ans = 0
            for j in range(1, len(A)):
                while A[j] - A[i] > diff:
                    i += 1
                ans += j - i
            return ans
        l, r = 0, A[-1] - A[0]
        k += 1
        while l <= r:
            mid = (l + r) // 2
            if count_not_greater(mid) >= k:
                r = mid - 1
            else:
                l = mid + 1
        return l
kennyxcao commented 3 years ago

Kth Pair Distance

Problem Source

Intuition

Code

class Solution {
  solve(nums, k) {
    const n = nums.length;
    nums.sort((a, b) => a - b);
    let left = 0;
    let right = nums[n - 1] - nums[0]; // max diff
    k += 1; // zero based -> one based
    while (left < right) {
      const mid = left + ~~((right - left) / 2);
      if (this.countNotGreater(nums, mid) >= k) {
        right = mid;
      } else {
        left = mid + 1;
      }
    }
    return left;
  }

  countNotGreater(nums, diff) {
    let count = 0;
    let left = 0;
    for (let i = 1; i < nums.length; ++i) {
      while (nums[i] - nums[left] > diff) {
        left += 1;
      }
      count += i - left;
    }
    return count;
  }
}

Complexity Analysis

RonghuanYou commented 3 years ago

思路


class Solution:
    def solve(self, nums, k):
        nums.sort()        
        def count(diff):
            i = ans = 0
            for j in range(1, len(nums)):
                while nums[j] - nums[i] > diff:
                    i += 1
                ans += j - i
            return ans

        left, right = 0, nums[-1] - nums[0]
        k += 1
        while left <= right:
            mid = (left + right) // 2
            if count(mid) >= k:
                right = mid - 1
            else:
                left = mid + 1

        return left
liuyangqiQAQ commented 3 years ago
class Solution {
    public int solve(int[] nums, int k) {
        k++;
        if(k < 0) return -1;

        Arrays.sort(nums);
        //配合二分
        int left = 0, right = nums[nums.length - 1] - nums[0];
        while (left < right) {
            int mid = left + (right - left) / 2;
            if(countGre(mid, nums) >= k) {
                right = mid;
            }else {
                left = mid + 1;
            }
        }
        return left;
    }

    public long countGre(int diff, int[] nums) {
        int i = 0;
        //使用long类型防止溢出
        long ans = 0;
        for (int j = 1; j < nums.length; j++) {
            while (nums[j] - nums[i] > diff) {
                i++;
            }
            ans += j - i;
        }
        return ans;
    }
}
july-aha commented 3 years ago
class Solution {
public:
    bool check(vector<int> a, int k, int x) {
        //printf("mid = %d\n", x);
        int res = 0;
        int index = 1;
        for(int i = 0; i < a.size(); i++) {
            int j = index;
            while(j < a.size() && a[j] <= a[i] + x) {
                j++;
            }
            //printf("out, j = %d\n", j);
            res += j - i - 1;
            index = j;
            //printf("res = %d, index = %d\n", res, index);
        }
        //res >= k ? printf("Return True\n\n") : printf("Return False\n\n");
        return res >= k;
    }

    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int l = 0, r = int(nums.back()) - nums[0];
        while(l <= r) {
            int mid = l + ((r - l) >> 1);
            if(check(nums, k, mid)) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return l;
    }
};
52HzEcho commented 3 years ago
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var smallestDistancePair = function (nums, k) {
    nums.sort((a, b) => a - b)
    let len = nums.length
    //二分法的左右取距离对的最小和最大值 [0,nums[len-1]-nums[0]]
    let left = 0, right = nums[len - 1] - nums[0]
    while (left <= right) {
        let mid = Math.floor((left + right) / 2)
        let cur = 0;  //小于mid的数量
        let start = 0;// 双指针中的左指针,属于循环中的第一个值
        // 以 i 为终点求距离差小于 mid 的数量
        for (let i = 1; i < len; i++) {
            while (nums[i] - nums[start] > mid) {
                start++
            }
            cur += i - start
        }
        if (cur < k) {
            left = mid + 1
        }else{
            right = mid - 1
        }

    }
    return left
};
15691894985 commented 3 years ago

【day 41】822. Kth-Pair-Distance

https://binarysearch.com/problems/Kth-Pair-Distance

思路:之前看了讲义思路, 动态求极值,求第k大,小于等于diff的值恰好有k个

class Solution:
    def solve(self, nums, k):
        nums.sort()
        def count_not_greater(diff):
            i = ans = 0
            for j in range(1, len(nums)):
                while nums[j] - nums[i] > diff:
                    i += 1
                ans += j - i
            return ans

        l, r = 0, max(nums) - min(nums)
        k +=1 
        while l <= r:
            mid = (l + r) >> 1
            if count_not_greater(mid)>=k:
                # 收缩右边界# 搜索区间变为 [left, mid - 1]
                r = mid - 1;
            else:# 搜索区间变为 [mid+1, right]
                l = mid + 1
        return l

时间复杂度:

flame0409 commented 3 years ago
class Solution {
    public int solve(int[] nums, int k) {
        k++;
        if(k < 0) return -1;

        Arrays.sort(nums);
        //配合二分
        int left = 0, right = nums[nums.length - 1] - nums[0];
        while (left < right) {
            int mid = left + (right - left) / 2;
            if(countGre(mid, nums) >= k) {
                right = mid;
            }else {
                left = mid + 1;
            }
        }
        return left;
    }

    public long countGre(int diff, int[] nums) {
        int i = 0;
        //使用long类型防止溢出
        long ans = 0;
        for (int j = 1; j < nums.length; j++) {
            while (nums[j] - nums[i] > diff) {
                i++;
            }
            ans += j - i;
        }
        return ans;
    }
}
sxr000511 commented 3 years ago

/**

codingcai commented 3 years ago

实现

class Solution:
    def solve(self, nums, k):            
        nums.sort()
        k += 1
        left, right = 0, nums[-1] - nums[0]
        while (left < right):
            mid = (left + right) >> 1
            if countPairs(mid) >= k:
                right = mid
            else:
                left = mid + 1
        return left

    def countPairs(diff):
        i = 0
        ans = 0
        for j in range(1, len(nums)):
            while (nums[j] - nums[i] > diff):
                i += 1   
            ans += j - i
        return ans
KennethAlgol commented 3 years ago
class Solution {
    public int smallestDistancePair(int[] nums, int k) {
        Arrays.sort(nums);
        int n = nums.length, low = 0, hi = nums[n-1] - nums[0];
        while (low < hi) {
            int cnt = 0, j = 0, mid = (low + hi)/2;
            for (int i = 0; i < n; ++i) {
                while (j < n && nums[j] - nums[i] <= mid) ++j;
                cnt += j - i-1;
            }
            if (cnt >= k) 
                hi = mid;

            else low = mid + 1;
        }

        return low;
    }
}
joeytor commented 3 years ago

思路

先排序数组

二分搜索的范围是 [0, nums[-1] - nums[0]], 即 distance 可能的 最大和最小值

​ 如果 distance 小于等于 mid 的 pair 数量 比 k 小, 那么搜索 右半区间, l = mid + 1

​ 如果 distance 小于等于 mid 的 pair 数量 大于等于 k, 那么搜索 左半区间, r = mid - 1

最后返回 l

用双指针法计算 当 nums 中 小于等于 mid 的 pair 数量

​ 遍历 j in [1, length), 如果 nums[j] - nums[i] > mid, i += 1, 知道 nums[j] - nums[i] ≤ mid,

​ 此时 count += j-i , 因为 是 sorted list, 所以如果 i 满足条件, 那么 [i, j) 直接到数都比 nums[i] 大, 所以也都符合条件, 加入 count

​ 下一个 j 也可以直接从 i 开始, 因为 i 之前的数都比 i 小, 所以 nums[j] - nums[i] 肯定会更大, 就会不符合条件

class Solution:
    def smallestDistancePair(self, nums: List[int], k: int) -> int:
        nums.sort()
        length = len(nums)

        # count the number of pairs that's less than equal to mid
        def less_than_equal(mid):
            i = 0
            count = 0
            for j in range(1, length):
                while i < j and nums[j] - nums[i] > mid:
                    i += 1
                count += j - i
            return count

        l, r = 0, nums[-1] - nums[0]
        while l <= r:
            mid = (l+r) >> 1
            if less_than_equal(mid) < k:
                l = mid + 1
            else:
                r = mid - 1
        return l

复杂度

n 为 数组长度, m 为 最大距离 nums[-1] - nums[0]

时间复杂度: O(nlogn + nlogm) 排序的时间复杂度是 O(nlogn), 二分时间复杂度 O(logw), 统计小于 mid 双指针时间复杂度 O(n)

空间转发的: O(n) sort 的时间复杂度

xinhaoyi commented 3 years ago

import java.util.*;

class Solution { public int solve(int[] nums, int k) { k++; Arrays.sort(nums); int lhs = 0; int rhs = nums[nums.length - 1] - nums[0]; while (lhs < rhs) { int mid = (lhs + rhs) / 2; long amt = 0; int leftp = 0; for (int i = 1; i < nums.length; i++) { while (nums[i] - nums[leftp] > mid) leftp++; amt += i - leftp; } if (amt >= k) rhs = mid; else lhs = mid + 1; } return lhs;

}

}

ymkymk commented 3 years ago

思路

二分法

代码

``

public class Solution extends VersionControl {
    public int solve(int[] nums, int k) {
        Arrays.sort(nums);

        int start = 0;
        int end = nums[nums.length - 1] - nums[0];
        k += 1;
        while (start <= end) {
            int mid = start + (end - start) / 2;
            if (countNoGreaterThan(nums, mid) >= (long) k) {
                end = mid - 1;
            } else {
                start = mid + 1; 
            }
        }
        return start;
    }

    public long countNoGreaterThan(int[] nums, int x) {
        long count = 0;
        int i = 0;
        for (int j = 1; j < nums.length; j++) {
            while (nums[j] - nums[i] > x) {
                i++;
            }
            count += j - i;
        }
        return count;
    }
}

复杂度分析

时间复杂度:O(nlogn)

空间复杂度:O(1)

hellowxwworld commented 3 years ago

思路

二分 使中间值逐渐逼近 kth 大 target

代码


bool check(vector<int>& nums, int diff, int k) {
    const int N = nums.size();
    long count = 0;
    int i = 0, j = 0;
    while (i < N || j < N) {
        while (j < N && nums[j] - nums[i] <= diff) j++;
        count += j - i - 1;
        i++;
    }
    return count >= k;
};

int solve(vector<int>& nums, int k) {
    const int N = nums.size();
    sort(nums.begin(), nums.end());

    k += 1;
    int left = 0, right = nums[N - 1] - nums[0];
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (check(nums, mid, k))
            right = mid;
        else
            left = mid + 1;
    }
    return left;
}
```cpp
lxy030988 commented 3 years ago

思路

代码 js

function solve(nums, k) {
  nums.sort((a, b) => a - b)
  let l = 0,
    r = nums[nums.length - 1] - nums[0],
    mid
  while (l <= r) {
    mid = (l + r) >> 1
    if (getCount(nums, mid) < k) {
      l = mid + 1
    } else {
      r = mid - 1
    }
  }
  return l
}

function getCount(nums, distance) {
  let l = 0,
    count = 0
  for (let i = 0; i < nums.length; i++) {
    while (nums[i] - nums[l] > distance) {
      l++
    }
    count += i - l
  }
  return count
}

复杂度分析

kbfx1234 commented 3 years ago

Kth Pair Distance

// 10-20 cpp  二分+双指针
long long help (vector<int>& nums, int mid) {
    int i = 0;
    long long cnt = 0;

    for (int j = 1; j < nums.size(); j++) {
        while(nums[j] - nums[i] > mid) ++i;
        cnt += (j - i); // 记录<=d的个数
    }
    return cnt;
}

int solve(vector<int>& nums, int k) {
    sort(nums.begin(), nums.end());
    int n = nums.size();
    int l = 0, r = nums[n-1] - nums[0];
    while (l <= r) {
        int mid = l + (r -l) / 2;
        if (help(nums, mid) > k) r = mid - 1;
        else l = mid + 1;
    }
    return l;
}
zszs97 commented 3 years ago

开始刷题

题目简介

【Day 41 】2021-10-20 - 822. Kth-Pair-Distance

题目思路

题目代码

代码块

class Solution:
    def solve(self, A, k):
        A.sort()
        def count_not_greater(diff):
            i = ans = 0
            for j in range(1, len(A)):
                while A[j] - A[i] > diff:
                    i += 1
                ans += j - i
            return ans
        l, r = 0, A[-1] - A[0]
        k += 1 # zero based -> one based
        while l <= r:
            mid = (l + r) // 2
            if count_not_greater(mid) >= k:
                r = mid - 1
            else:
                l = mid + 1
        return l

复杂度

V-Enzo commented 3 years ago

思路

  1. 先对数组进行排序,然后二分其中的距离,进行判读是否满足第k个要求。
  2. k在此题中是从1开始,而不是从0开始,所以上来要先加k。
  3. 判断能否覆盖要使用while会比双for要好
  4. 在算mid时,要用start+(end-start)/2, 而不是start+(end-start)*1/2, 否则会超时。
    long findpairs(vector<int>& nums, int val){
    int N = nums.size();
    long count = 0;
    int i=0, j=0;
    while(i<N || j<N){
        while(j<N && nums[j]-nums[i]<= val) j++;
        count += j-i-1;
        i++;
    }
    return count;
    }
    int solve(vector<int>& nums, int k) {
    sort(nums.begin(), nums.end());
    int n = nums.size();
    k+=1;
    int start=0;
    int end = nums[n-1] - nums[0];
    while(start<=end){
        int mid = start + (end -start)/2;
        if(findpairs(nums, mid)>=k){
            end = mid-1;
        }else{
            start = mid+1;
        }
    }
    return start;
    }

    Complexity:

    Time:O(logn) Space:O(1)

QiZhongdd commented 3 years ago
function solve(nums,k){
  nums.sort();
  let l=0,r=nums[nums.length-1]-nums[0]
  function find(mid){
    let ans=0,i=0;
    for (let j=1; j<nums.length; j++){
      while (nums[j] - nums[i] > mid){
        i += 1
      }   
      ans += j - i
    }
    console.log(ans)
    return ans
  }
  while(l<=r){
    let mid=l+((r-l)>>1)
    if(find(mid)>=k){
      r=mid-1
    }else{
      l=mid+1
    }
  }
  return l
}
JAYWX commented 3 years ago
class Solution:
    def solve(self, A, k):
        A.sort()
        def count_not_greater(diff):
            i = ans = 0
            for j in range(1, len(A)):
                while A[j] - A[i] > diff:
                    i += 1
                ans += j - i
            return ans
        l, r = 0, A[-1] - A[0]
        k += 1 # zero based -> one based
        while l <= r:
            mid = (l + r) // 2
            if count_not_greater(mid) >= k:
                r = mid - 1
            else:
                l = mid + 1
        return l
lizzy-123 commented 3 years ago

思路 直接线求距离,然后排序后直接得到第k个距离,但是此方法超时了

int solve(vector<int>& nums, int k) {
    int n = nums.size();
    std::vector<int> results;

    for (int i = 0; i < n-1;i++)
    {
        for (int j = i + 1; j < n;j++)
        {
            int d = nums[i] - nums[j];
            results.push_back(abs(d));
        }
    }

    sort(results.begin(), results.end());

    return results[k];

}

复杂度分析 时间复杂度:O(n*n) 空间复杂度:O(n)

因为超时,所以不能直接使用暴力搜索,需要使用二分法,需要先对数组拍需,查找第k小距离

chaggle commented 3 years ago

title: "Day 41 822. Kth-Pair-Distance" date: 2021-10-20T20:22:16+08:00 tags: ["Leetcode", "c++", "Binary Search"] categories: ["91-day-algorithm"] draft: true


822. Kth-Pair-Distance

题目

Given a list of integers nums and an integer k, return the k-th (0-indexed) smallest abs(x - y) for every pair of elements (x, y) in nums. Note that (x, y) and (y, x) are considered the same pair.

Constraints

n ≤ 100,000 where n is the length of nums
Example 1
Input
nums = [1, 5, 3, 2]
k = 3
Output
2
Explanation
Here are all the pair distances:

abs(1 - 5) = 4
abs(1 - 3) = 2
abs(1 - 2) = 1
abs(5 - 3) = 2
abs(5 - 2) = 3
abs(3 - 2) = 1
Sorted in ascending order we have [1, 1, 2, 2, 3, 4].

题目思路

  • 1、题目为求数组排序之后第 k 大的距离,首先排序数组,最大的距离即为数组头与尾值之差, 最小的距离当然为0,在此范围内去找寻一个合理的值即可。
  • 2、使用二分法,每次取 mid = l + (r - l) / 2,可以减少查找的时间复杂度,不然使用计数的话,时间复杂度在O($n^2$)。
  • 3、本题目也可以使用数学的思想去考虑,所有的距离对为一共有$n(n-1)$,所以只要大于mid距离的对数有$n(n-1)$ - k,即为不合理。
class Solution {
public:
    bool isvaild(vector<int>& nums, int dis, int k)
    { 
        int n = nums.size()
        long long int count = 0;
        int i = 0, j = 0;
        while (i < n || j < n)
        {
            while (j < n && nums[j] - nums[i] <= dis) j++;
            count += j - i - 1;
            i++;
        }
        return count >= k;
    };

    int solve(vector<int>& nums, int k) {
        int n = nums.size();
        k++;
        sort(nums.begin(), nums.end());
        int l = 0, r = nums[n - 1] - nums[0]; 
        while(l < r) 
        {
            int mid = l + (r - l) / 2;
            int count = 0, left = 0;
            if(isvaild(nums, mid, k)) r = mid;
            else l = mid + 1;
        }
        return l;
}

复杂度

Lydia61 commented 3 years ago

Kth-Pair-Distance

思想

排序

代码

 class Solution:
    def solve(self, nums, k):
        distances = []
        for m in range(len(nums)-1):
            for n in range(m+1, len(nums)):
                distances.append(abs(nums[n] - nums[m]))
        return sorted(distances)[k-1]

复杂度分析

newbeenoob commented 3 years ago

思路:


见代码注释:

var smallestDistancePair = function(nums, k) {

    nums.sort((a , b) => a - b);

    const check = (nums , diff , k) => {
        // sliding window
        // 计数 距离对 小于等于 diff 的 pairs 数量
        let count = 0;
        let j = 0;
        for(let i = 1 ; i < nums.length ; ++i) {
            // j = 0
            // 无需复位 j ,因为 数组已经单调递增 若 nums[i] - nums[0] 不满足 <= diff
            // nums[i+1] - nums[0] 一定不满足

            while(nums[i] - nums[j] > diff) ++j;

            // 由于数组有序 所以 i - j 就是当前轮满足的pair数
            count += i - j;
        }
        return count >= k;
    }

    let l = 0;
    let r = nums[nums.length - 1] - nums[0];
    let mid;
    while(l < r) {
        mid = l + ((r - l) >> 1);
        if(check(nums , mid , k)) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }   

    return l;
};

复杂度分析


标签


二分 , 双指针 , 滑动窗口

vincentLW commented 3 years ago
int solve(vector<int>& nums, int k) {
    int n = nums.size();
    std::vector<int> results;

    for (int i = 0; i < n-1;i++)
    {
        for (int j = i + 1; j < n;j++)
        {
            int d = nums[i] - nums[j];
            results.push_back(abs(d));
        }
    }

    sort(results.begin(), results.end());

    return results[k];

}
复杂度分析
时间复杂度:O(n*n)
空间复杂度:O(n)
Francis-xsc commented 3 years ago

思路

二分

代码


int count_not_greater(vector<int>a,int diff)
{
    int ans=0,len=a.size();
    int i=0;
    for(int j=1;j<len;j++)
    {
        while(a[j]-a[i]>diff)
            i++;
        ans+=(j-i);
    }
    return ans;
}
int solve(vector<int>& nums, int k) {
    sort(nums.begin(),nums.end());
    int l=0,r=nums.back()-nums[0];
    while(l<=r)
    {
        int mid=l+(r-l)/2;
        int t=count_not_greater(nums,mid);
        if(t>k)
            r=mid-1;
        else
            l=mid+1;
    }
    return l;
}

复杂度分析

ymwang-2020 commented 3 years ago
 class Solution:
    def solve(self, nums, k):
        nums_sort=sorted(nums)
        ans=[]
        def count(diff):
            i=ans=0
            for j in range(1,len(nums)):
                while nums[j]-nums[i]>diff:
                    i+=1
                ans+=j-i
            return ans
        l=0
        r=nums[-1]-num[0]
        k+=1
        while l <= r:
            mid = (l + r) // 2
            if count(mid) >= k:
                r = mid - 1
            else:
                l = mid + 1
        return l
MissNanLan commented 3 years ago

前置知识

思路

经过提点,我终于知道题目是个啥意思了。就是在返回的数组 abs(x-y)中找出 k 个小的元素。拿例子来说就是,k=3,那么 2 就是第三小的数

代码


class Solution {
    solve(nums = [], k) {
        nums.sort()
        const n = nums.length
        k += 1
        let start = 0
        let end = nums[n - 1] - nums[0] // 这里之前没想到,不过用笔画下就知道了。假设nums为[1,2,5,10],你想想r是不是nums[n-1]-nums[0]
        while (start <= end) {
            let mid = start + (end - start) >> 1;
            if (this.findpairs(nums, mid) >= k) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
        return start;
    }

    findpairs(nums, val) {
        const N = nums.length
        let count = 0;
        let i = 0, j = 0;
        while (i < N || j < N) {
            while (j < N && nums[j] - nums[i] <= val) {
                j++;
            }
            count += j - i - 1;
            i++;
        }
        return count;
    }

}