LeetCode719. Find K-th Smallest Pair Distance (二分法,滑动窗口优化)

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:

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

解法

本题容易想到用朴素方法求解,先排序,后取k大。得到MLE

class Solution {
public:
    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        vector<int> arr;
        for(int i=0;i<nums.size()-1;i++)
            for(int j=i+1;j<nums.size();j++) {
                arr.push_back(nums[j]-nums[i]);
            }
        sort(arr.begin(), arr.end());
        return arr[k-1];
    }    
};

改用了优先队列,如果太多数字了则去掉一些,得到TLE

class Solution {
public:
    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        priority_queue<int> que;
        for(int i=0;i<nums.size()-1;i++)
            for(int j=i+1;j<nums.size();j++){
                que.push(nums[j]-nums[i]);
                if(que.size()>k+1) 
                    que.pop();
            }
        while(que.size()!=k) 
            que.pop();
        return que.top();
    }    
};

后来想了一个方法,可惜是错误的。。折腾不明白看了别人的博客,发现本题的解法是二分法。这个题目的二分法“分”得很特殊,不是按数组下标来分的,而是用问题的结果来分的(第k大对)。
二分法中,上界是0(存在一个数字相同的pair),下界是是数组的最大值减去最小值。
找数组中有多少比(low+mid)/2 小的pair,如果得到的结果比k要大,说明结果就在[low, mid],如果要小则在[mid+1, high]中。

class Solution {
public:
    int getLessthanKcount(vector<int>& nums, int k) {
        // 朴素法求解nums中有多少对数字<=k
        int cnt=0;
        for(int right=1;right<nums.size();right++) {
            int left=right-1;
            while(left>=0&&nums[right]-nums[left]<=k){
                cnt++;
                left--;
            }

        }
        return cnt;
    }
    
    int getLessthanKcount_opt(vector<int>& nums, int k) {
        //search
       //使用窗口思想,判断差值<=k的个数,r-l即表示[l,r]间间隔<m的个数(每确定一个窗口就新增加了(r-l+1)- 1个差值对)
       int left = 0;
       int count = 0;
       for(int right = 0;right<nums.size();right++){
           while(nums[right] - nums[left]>k){
               left++;
           }
           count+= right-left;
       }
        return count;
    }
    
    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int lo=0, hi=*(nums.end()-1)-*nums.begin();
        while(lo < hi) {
            int mid = (lo+hi)/2;
            int cnt = getLessthanKcount_opt(nums, mid);
            if(cnt>=k)
                hi = mid;
            else if(cnt<k)
                lo = mid+1;
        }
        return lo;
    }
};

本题还有一个巧妙的方法,就是暴力解,用空间来解决这个问题。

class Solution 
{
public:
    int smallestDistancePair(vector<int>& nums, int k) 
    {
        int n = nums.size(), N = 1000000;
        vector<int> count(N, 0);
        for (int i = 0; i < n; i++)
        {
            for (int j = i + 1; j < n; j++)
            {
                count[abs(nums[i] - nums[j])]++;
            }
        }

        for (int i = 0; i < count.size(); i++)
        {
            if (count[i] >= k)
                return i;
            else
                k -= count[i];
        }
        return -1;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_26973089/article/details/83688302