题目描述:我们有一个由平面上的点组成的列表 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;
}
}