We have a list of points
on the plane. Find the K
closest points to the origin (0, 0)
.
(Here, the distance between two points on a plane is the Euclidean distance.)
You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in.)
Example 1:
Input: points = [[1,3],[-2,2]], K = 1 Output: [[-2,2]] Explanation: The distance between (1, 3) and the origin is sqrt(10). The distance between (-2, 2) and the origin is sqrt(8). Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]].
Example 2:
Input: points = [[3,3],[5,-1],[-2,4]], K = 2 Output: [[3,3],[-2,4]] (The answer [[-2,4],[3,3]] would also be accepted.)
这道题目是一个很经典的问题,即找到前k个最小的元素。这里的大小由该点到原点的距离来定义。
-------------------------------------------------------------------------------------------------------------------------------------
首先第一种办法:最简单的,上来就排序。排完序之后取前k个就行了
代码如下:
其中用到了java 8中的->来实现comparator的传递
public int[][] kClosest(int[][] points, int K) {
Arrays.sort( points,(p1,p2)->p1[0]*p1[0]+p1[1]*p1[1]-p2[0]*p2[0]-p2[1]*p2[1] );
return Arrays.copyOfRange(points,0,K);
}
---------------------------------------------------------------------------------------------------------------------------------
第二种办法,调用优先队列,维持一个容量为k的最大堆(堆顶元素为最大值)
对于进来的每一个元素,先将该元素插入最大堆(时间复杂度为O(lgK)),然后将最大堆顶元素扔掉(常数时间)。
即每个操作为O(lgK)
总共N个元素,故为O(NlgK)
代码如下
public int[][] kClosest(int[][] points, int K) {
//优先队列默认是最小堆,这里通过comparator设为最大堆(堆顶为最大值)
PriorityQueue<int[]> pq = new PriorityQueue<int[]>( (p1,p2)->p2[0]*p2[0]+p2[1]*p2[1]-p1[0]*p1[0]-p1[1]*p1[1] );
for(int[] point:points){
pq.offer(point);//将元素插入优先队列
if(pq.size()>K){
pq.poll();//扔掉队列里的最大元素
}
}
int[][] ret = new int[K][2];
while(K>0){
ret[--K] = pq.poll();
}
return ret;
}
-------------------------------------------------------------------------------------------------------------------------------
第三种方法:基于切分的方法。
类似于快速排序中的切分
import java.util.*;
class Solution {
public int[][] kClosest(int[][] points, int K) {
int low = 0;
int high = points.length-1;
while(low<high){
int j = partition(points,low,high);
if(j==K){
break;
}
else if(j<K){
low = j+1;
}
else if(j>K){
high = j-1;
}
}
return Arrays.copyOfRange(points, 0, K);
}
private int partition(int[][] points,int low,int high) {
int i = low;
int j= high+1;
while(true){
while( less(points[++i],points[low]) ){
if(i==high) break;
}
while( less(points[low],points[--j]) ){
// if(j==low) break;
}
if(i>=j){
break;
}
swap(points,i,j);
}
swap(points,low,j);
return j;
}
private boolean less(int[] p1, int[] p2) {
return (p1[0] * p1[0] + p1[1] * p1[1] - p2[0] * p2[0] - p2[1] * p2[1])<0;
}
private void swap(int[][] points,int i ,int j){
int[] t = points[i];
points[i] = points[j];
points[j] = t;
}
private void shuffle(int[][] points) {
Random random = new Random();
for(int ind = 1; ind < points.length; ind++) {
int r = random.nextInt(ind + 1);
swap(points, ind, r);
}
}
}