1 2 3 4 5 6 7 8 9 10
| class Solution { public int[][] kClosest(int[][] points, int k) { Arrays.sort(points, (a, b) -> squaredDistance(a) - squaredDistance(b)); return Arrays.copyOf(points, k); }
public int squaredDistance(int[] point) { return point[0] * point[0] + point[1] * point[1]; } }
|
第一种方法是最直接的, 直接算出每个点到原点的距离. 然后排序即可.
时间复杂度: O(NlogN) 快速排序的平均时间.
空间复杂度: O(logN) 也就是快速排序栈的空间.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class Solution { public int[][] kClosest(int[][] points, int k) { quickSelect(points, 0, points.length - 1, k - 1); return Arrays.copyOf(points, k); }
public void quickSelect(int[][] points, int start, int end, int k) { while (true) { int pivot = start, left = start + 1, right = end; int pivotDistance = squaredDistance(points[pivot]); while (left <= right) { int leftDistance = squaredDistance(points[left]); int rightDistance = squaredDistance(points[right]); if (leftDistance > pivotDistance && rightDistance < pivotDistance) { swap(points, left, right); left += 1; right -= 1; continue; } if (leftDistance <= pivotDistance) { left += 1; } if (rightDistance >= pivotDistance) { right -= 1; } } swap(points, pivot, right); if (right == k) return; else if (right < k) { quickSelect(points, right + 1, end, k); return; } else { quickSelect(points, start, right - 1, k); return; } } }
private int squaredDistance(int[] point) { return point[0] * point[0] + point[1] * point[1]; }
private void swap(int[][] points, int i, int j) { int[] temp = points[i]; points[i] = points[j]; points[j] = temp; } }
|
这是第二种方法. 看到第几个最大最小的时候, 我们就应该想到用quick select. 唯一需要注意的是第49行和第52行的return不要忘了. 因为从48或51行成功返回后代表着第k个数字已经就位, 此时就应该返回了. 原来我是忘记写return结果是被一直卡在循环里面了.
时间复杂度: O(N) 其实就是quick select的时间复杂度. 如果每次恰好平分给定的区间, 那么每次要用的时间为:
N + N / 2 + N /4 + N / 8 + …. 这个series converge to 2N, 因此就是O(2N)也就是O(N).
空间复杂度: O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public int[][] kClosest(int[][] points, int k) { PriorityQueue<int[]> q = new PriorityQueue<>( (a, b) -> Integer.compare(b[0] * b[0] + b[1] * b[1], a[0] * a[0] + a[1] * a[1])); for (int[] point : points) { q.offer(point); if (q.size() > k) { q.poll(); } } int[][] result = new int[k][2]; for (int i = 0; i < k; i++) { result[i] = q.poll(); } return result; } }
|
第三种方法用max heap. 这个道理也简单. 给一个heap里面加point, 等到heap的大小大于k的时候, 我们把这个heap里面最大的距离的那个点去掉即可. 一直到我们遍历完所有的点, heap中剩下的就是最小的那k个. 然后再把他们poll出来就完事儿了.
最大的启发就是PriorityQueue是个heap其实. 更generalize一下就是你给它一个标准, 它按照这个标准比较. 是给它Comparator来告诉它如何比较. a和b, 如果出来的比较结果大于0, 那么a就靠前, 否则就是b.
时间复杂度: O(Nlogk) 这个应该就是heap的插入, 移除元素的时间复杂度吧.
空间复杂度: O(k)
更难的就是手动写一个heap.