[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:

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

 

這道題給了我們一個數組,讓我們找第k小的數對兒距離,數對兒距離就是任意兩個數字之間的絕對值差。那么我們先來考慮最暴力的解法,是不是就是遍歷任意兩個數字,算出其絕對值差,然后將所有距離排序,取第k小的就行了。But,OJ 搖着頭說圖樣圖森破。但是我們可以在純暴力搜索的基礎上做些優化,從而讓 OJ 說 YES。那么下面這種利用了桶排序的解法就是一種很好的優化,題目中給了數字的大小范圍,不會超過一百萬,所以我們就建立一百萬個桶,然后還是遍歷任意兩個數字,將計算出的距離放到對應的桶中,這里桶不是存的具體距離,而是該距離出現的次數,桶本身的位置就是距離,所以我們才建立了一百萬個桶。然后我們就可以從0開始遍歷到一百萬了,這樣保證了我們先處理小距離,如果某個距離的出現次數大於等於k了,那么我們返回這個距離,否則就用k減去這個距離的出現次數,參見代碼如下:

 

解法一:

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

 

上面的解法雖然逃脫了 OJ 的魔掌,但也僅僅是險過,並不高效。我們來看一種基於二分搜索的解法。這道題使用的二分搜索法是博主歸納總結帖 LeetCode Binary Search Summary 二分搜索法小結 中的第四種,即二分法的判定條件不是簡單的大小關系,而是可以抽離出子函數的情況,下面我們來看具體怎么弄。我們的目標是快速定位出第k小的距離,那么很適合用二分法來快速的縮小查找范圍,然而最大的難點就是如何找到判定依據來折半查找,即如果確定搜索目標是在左半邊還是右半邊。做過 Kth Smallest Element in a Sorted Matrix 和 Kth Smallest Number in Multiplication Table 這兩道題的同學應該對這種搜索方式並不陌生。核心思想是二分確定一個中間數,然后找到所有小於等於這個中間數的距離個數,用其跟k比較來確定折半的方向。具體的操作是,我們首先要給數組排序,二分搜索的起始 left 為0,結束位置 right 為最大距離,即排序后的數字最后一個元素減去首元素。然后進入 while 循環,算出中間值 mid,此外我們還需要兩個變量 cnt 和 start,其中 cnt 是記錄小於等於 mid 的距離個數,start 是較小數字的位置,均初始化為0,然后我們遍歷整個數組,先進行 while 循環,如果 start 未越界,並且當前數字減去 start 指向的數組之差大於 mid,說明此時距離太大了,我們增加減數大小,通過將 start 右移一個,那么 while 循環退出后,就有 i - start 個距離小於等於 mid,將其加入 cnt 中,舉個栗子來說:

   1    2    3    3    5

start              i

mid = 2

如果 start 在位置0,i在位置3,那么以 nums[i] 為較大數可以產生三個(i - start)小於等於 mid 的距離,[1 3], [2 3], [3 3],這樣當i遍歷完所有的數字后,所有小於等於 mid 的距離的個數就求出來了,即 cnt。然后我們跟k比較,如果其小於k,那么 left 賦值為 mid+1,反之,則 right 賦值為 mid。最終返回 right 或 left 均可,參見代碼如下:

 

解法二:

class Solution {
public:
    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int n = nums.size(), left = 0, right = nums.back() - nums[0];
        while (left < right) {
            int mid = left + (right - left) / 2, 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;
        }
        return right;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/719

 

類似題目:

Find K Pairs with Smallest Sums

Kth Smallest Element in a Sorted Matrix

Find K Closest Elements

Kth Smallest Number in Multiplication Table

K-th Smallest Prime Fraction

 

參考資料:

https://leetcode.com/problems/find-k-th-smallest-pair-distance/solution/

https://leetcode.com/problems/find-k-th-smallest-pair-distance/discuss/109077/C++-counting-sort-O(n2)-and-binary-search-O(nlogn)

https://leetcode.com/problems/find-k-th-smallest-pair-distance/discuss/109082/Approach-the-problem-using-the-%22trial-and-error%22-algorithm

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM