LeetCode 719.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:
2 <= len(nums) <= 10000
.0 <= nums[i] < 1000000
.1 <= k <= len(nums) * (len(nums) - 1) / 2
.
/**********************************************************************/
最近做了好些動態規劃的題目,而且都是一上來覺得完全ojbk用naive演算法就可以做出來,最後發現不用動態規劃就一定會超時限,最後灰溜溜地用動態規劃實現了一遍。
所以這道題目我一上來就想著用動態規劃,活生生把思路帶歪,把題目想難了。
- 其實這題目很簡單,用最naive的方法就可以解出來。因為題幹陣列nums裡每個值都限定在1e6以內,因此建一個1e6大小的陣列(我起名叫distance),遍歷n次nums陣列並計算出來每兩個單元之間的差,然後在distance數組裡把這個差值對應的點加1就可以了。最後遍歷一遍distance陣列就可以找到第k小的差值。這種方法只需要O(n*n+1e6)的時間複雜度。當然不快,最後在Leetcode裡用時排在後20%,但是貴在簡單。
- 再介紹一個更快的演算法,是我看LeetCode裡前10%瞭解到的。簡單來說,其核心就是先對整個nums陣列進行排序,得到差值的上限。然後就可以對這個上限和顯而易見的下限0用二分法來找指定的差值了。二分查詢的過程也很精妙:因為給任何一個數值t,在一個已經排序了的陣列上可以很容易並很快地找出所有比這個數值t小的差值的數量count。因此只要將count與題乾的k進行比較,就可以得到新的上下限了。
- 最後說一下我很愚蠢的動態規劃演算法:考慮DP[i][j][k]表示從nums[i]到nums[j]中的第k小的差值。可以發現,DP[i][j][k]的值等於——DP[i][j-1][1~k],DP[i+1][j][1~k]和|nums[i]-nums[j]|中第k小的值,並且還需要去除DP[i+1][j-1][1~k]中的重疊部分。因此當計算DP[i][j][1~k]時,只需要從DP[i][j-1][1],DP[i+1][j][1],DP[i+1][j-1][1] 開始,並和 |nums[i]-nums[j]| 一起進行比較,就能把DP[i][j][1~k]填滿。顯然,這樣的動態規劃演算法需要O(n*n*k)的時間複雜度,比naive的方法還要慢,18個用例裡有8個都超出時限了,非常愚蠢………………
最後附上程式碼:
首先是第一個方法的——
class Solution {
public int smallestDistancePair(int[] nums, int k) {
int distance[] = new int[1000000];
int ln = nums.length;
for(int i = 0;i<ln;i++)
for(int j = i+1;j<ln;j++)
{
distance[abs(nums[i]-nums[j])]++;
}
int result = 0;
while(k>0)
{
k-=distance[result];
result++;
}
return result-1;
}
public int abs(int a)
{
return a>=0?a:-a;
}
}
然後是動態規劃方法的程式碼(第二個方法我沒有實現,就不放程式碼了)——
class Solution {
public int smallestDistancePair(int[] nums, int k) {
int ln = nums.length;
int DP [][][] = new int[ln][ln][k+1];
for(int i = 0;i<ln-1;i++)
{
DP[i][i+1][1] = abs(nums[i] - nums[i+1]);
}
for(int step = 2;step<ln;step++)
{
for(int i = 0;i+step<ln;i++)
{
int Left = 1;
int Right = 1;
int extra = abs(nums[i]-nums[i+step]);
int Compare = 1;
for(int t = 1;t<=(step+1)*step/2 && t<=k;t++)
{
if(Left > step*(step-1)/2 || Left>k)
{
if(DP[i+1][i+step][Right]<=extra && Right<=step*(step-1)/2 && Right<=k)
{
if(Compare<=(step-2)*(step-1)/2 && Compare<=k && DP[i+1][i+step][Right]==DP[i+1][i+step-1][Compare])
{
Compare++;
Right++;
t--;
}
else{
DP[i][i+step][t] = DP[i+1][i+step][Right];
Right++;
}
}
else
{
DP[i][i+step][t] = extra;
extra = Integer.MAX_VALUE;
}
}
else if(Right > step*(step-1)/2 || Right>k)
{
if(DP[i][i+step-1][Left]<=extra && Left<=step*(step-1)/2 && Left<=k)
{
if(Compare<=(step-2)*(step-1)/2 && Compare<=k && DP[i][i+step-1][Left]==DP[i+1][i+step-1][Compare])
{
Compare++;
Left++;
t--;
}
else
{
DP[i][i+step][t] = DP[i][i+step-1][Left];
Left++;
}
}
else
{
DP[i][i+step][t] = extra;
extra = Integer.MAX_VALUE;
}
}
else
{
int min = min(DP[i][i+step-1][Left],DP[i+1][i+step][Right],extra);
if(min == 1)
{
if(Compare<=(step-2)*(step-1)/2 && Compare<=k && DP[i][i+step-1][Left]==DP[i+1][i+step-1][Compare])
{
Compare++;
Left++;
t--;
}
else
{
DP[i][i+step][t] = DP[i][i+step-1][Left];
Left++;
}
}
else if(min == 2)
{
if(Compare<=(step-2)*(step-1)/2 && Compare<=k && DP[i+1][i+step][Right]==DP[i+1][i+step-1][Compare])
{
Compare++;
Right++;
t--;
}
else
{
DP[i][i+step][t] = DP[i+1][i+step][Right];
Right++;
}
}
else
{
DP[i][i+step][t] = extra;
extra = Integer.MAX_VALUE;
}
}
}
}
}
return DP[0][ln-1][k];
}
public int abs(int a)
{
return a>=0?a:-a;
}
public int min(int a,int b,int c)
{
int min = 0;
min = a<b?(a<c?1:3):(b<c?2:3);
return min;
}
}