LeetCode719. Find K-th Smallest Pair Distance (二分法,滑動視窗優化)
阿新 • • 發佈:2018-11-20
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大。得到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;
}
};