題目描述:我們有一個由平面上的點組成的列表 points。需要從中找出 K 個距離原點 (0, 0) 最近的點。
(這裏,平面上兩點之間的距離是歐幾里德距離。)
你可以按任何順序返回答案。除了點座標的順序之外,答案確保是唯一的。
示例 1:
輸入:points = [[1,3],[-2,2]], K = 1
輸出:[[-2,2]]
解釋:
(1, 3) 和原點之間的距離爲 sqrt(10),
(-2, 2) 和原點之間的距離爲 sqrt(8),
由於 sqrt(8) < sqrt(10),(-2, 2) 離原點更近。
我們只需要距離原點最近的 K = 1 個點,所以答案就是 [[-2,2]]。
示例 2:
輸入:points = [[3,3],[5,-1],[-2,4]], K = 2
輸出:[[3,3],[-2,4]]
(答案 [[-2,4],[3,3]] 也會被接受。)
提示:
1 <= K <= points.length <= 10000
-10000 < points[i][0] < 10000
-10000 < points[i][1] < 10000
方法一:
暴力法
最直觀的方法就是將所有點到原點的距離都算出來,然後進行高效率的排序,然後找到第k大的距離。然後返回距離小於等於這個第K大距離的K個點
class Solution{
public int [] [] kClosest(int [] [] points,int k){
int n=points.length;
int [] dists=new int[n];
for(int i=0;i<n;i++)
{
dists[i]=dist(points[i]);
}
Arrays.sort(dists);
int disK=dists[k-1];
int [] [] ans=new int[k][2];
int t=0;
for(int i=0;i<n;i++)
if(dist(points[i])<=disK)
ans[t++]=points[i];
return ans;
}
public int dist(int [] point)
{
return point[0]*point[0]+point[1]*point[1];
}
}
方法二:
分治法+快速選擇劃分
建議先參考這裏的劃分原則,是Lomuto劃分
劃分實現代碼
int lomutopartition(int a[],int start,int end)
{
int p=a[start];//把數組的第一個元素作爲中軸p值
int s=start;
for(int i=start+1;i<=end;i++)//這裏的i++,表示在不停的在遍歷未處理段
//從i=start+1,開始,算法從左到右掃描子數組a,
//在每一次循環中,它把未知段中的第一個元素與中軸p進行比較
{
if(a[i]<p)//如果未知段的元素小於中軸p,小於p元素的段需要擴大
{
s++;//擴大,s++
swap(a,s,i);//交換a【i】和a【s】
}
}
swap(a,start,s);//未處理段爲空,都處理好了,交換中軸p值和a【s】
//這裏的a【s】是小於p值的段的最後一個元素,所以交換後
//中軸p的左邊的數都是小於p的,右邊都是大於等於p的。
return s;//返回此時中軸的下標
}
利用題目中可以按照任何順序返回 K 個點的條件
我們選擇第一個元素 x = A[i] 然後將數組分爲兩部分: 一部分是到原點距離小於 x 的,另一部分是到原點距離大於等於 x 的。 這個快速選擇的過程與快速排序中選擇一個關鍵元素將數組分爲兩部分的過程類似。
如果我們快速選擇一些關鍵元素,那麼每次就可以將問題規模縮減爲原來的一半。
舉一個例子
數組【4,1,10,8,7,12,9,2,15】.這裏k=5(前k個數,數組中的下標就是4),劃分步驟圖解如下
我們可以看到當劃分到s=k-1,s=4時,s左邊的子數組中的數都是小於s的,s處的數正好是第5個數,所以就可以直接返回了。
java語言實現
class Solution {
public static int[][] p;
public int[][] kClosest(int[][] points, int K) {
this.p = points;
work(0, p.length - 1, K);
return Arrays.copyOfRange(p, 0, K);
//複製數組arr,0下標開始, k下標結束. 但是不包括k,把這個二維數組看做k個一維數組來複制
//最後返回的還是二維數組。
}
public void work(int low,int high, int K)
{
if(low >= high) return;
int temp = dist(low);
int j = low;
for(int i = low+1 ; i < high+1 ; i++)
{
if(dist(i) <temp)
{
j++;
swap(i,j);
}
}
swap(low,j);
if(j-low+1 < K) work(j+1,high,K-(j-low+1));
else if(j-low + 1 > K) work(low,j-1,K);
else return;
}
public int dist(int i)
{
return p[i][0] * p[i][0] + p[i][1]*p[i][1];
}
public void swap(int i ,int j)
{
int t0 = p[i][0];
int t1 = p[i][1];
p[i][0] = p[j][0];
p[i][1] = p[j][1];
p[j][0] = t0;
p[j][1] = t1;
}
}
另外一種表達方式也可通過,參考以下博文快速排序
java實現
class Solution {
public static int[][] p;
public int[][] kClosest(int[][] points, int K) {
this.p = points;
work(0, p.length - 1, K);
return Arrays.copyOfRange(p, 0, K);
//複製數組arr,0下標開始, k下標結束. 但是不包括k,把這個二維數組看做k個一維數組來複制
//最後返回的還是二維數組。
}
public void work(int low,int high, int K)
{
if(low>=high)
return;
int p = dist(low);
int i=low;
int j = high+1;
do{
do{
i++;
if(i==high+1)
{
i=high;
break;
}
}while(dist(i)<p);
do{
j--;
}while(dist(j)>p);
swap(i,j);
}while(i<j);
swap(i,j);
swap(low,j);
if(j-low+1 < K) work(j+1,high,K-(j-low+1));
else if(j-low + 1 > K) work(low,j-1,K);
else return;
}
public int dist(int i)
{
return p[i][0] * p[i][0] + p[i][1]*p[i][1];
}
public void swap(int i ,int j)
{
int t0 = p[i][0];
int t1 = p[i][1];
p[i][0] = p[j][0];
p[i][1] = p[j][1];
p[j][0] = t0;
p[j][1] = t1;
}
}