本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、题目描述:
973. 最接近原点的 K 个点 - 力扣(LeetCode) (leetcode-cn.com)
给定一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点,并且是一个整数 k ,返回离原点 (0,0) 最近的 k 个点。
这里,平面上两点之间的距离是 欧几里德距离( √(x1 - x2)2 + (y1 - y2)2 )。
你可以按 任何顺序 返回答案。除了点坐标的顺序之外,答案 确保 是 唯一 的。
示例 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 <= 104
- -104 < xi, yi < 104
二、思路分析:
本质上和找第K大元素的思路是一样的,使用快排中的partition方法,可以快速找到第pivot+1大(pivot是方法返回的下标)的元素,时间复杂度是O(n),然后根据返回的pivot和 k 比较,采取类似二分查找的策略再分段筛查,直到返回了下标是k-1的pivot,也就是这个位置就是第k大,将其前面(包括这个)的元素返回即可。
这里有一点不一样的地方是比较大小的时候是算距离圆心的记录,勾股定理就好了。
三、AC 代码:
class Solution {
public int[][] kClosest(int[][] points, int k) {
int length = points.length;
int start = 0;
int end = length - 1;
int pivot;
while(true) {
pivot = partition(points, start, end);
if (pivot == k - 1) {
break;
} else if (pivot > k - 1) {
end = pivot - 1;
} else if (pivot < k - 1) {
start = pivot + 1;
}
}
int[][] result = new int[k][];
for (int i = 0; i < k; i ++) {
result[i] = points[i];
}
return result;
}
private int partition(int[][] points, int start, int end) {
int pivot = start;
for (int i = start; i <= end; i ++) {
if (compare(points[i], points[end]) <= 0) {
int[] tmp = points[pivot];
points[pivot] = points[i];
points[i] = tmp;
pivot ++;
}
}
return pivot - 1;
}
private int compare(int[] p1, int[] p2) {
return p1[0] * p1[0] + p1[1] * p1[1]
- p2[0] * p2[0] - p2[1] * p2[1];
}
}