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;
}
};