Skip to content

Commit 91e3052

Browse files
rain84yanglbme
andauthored
feat: add solutions to lc problem: No.0973 (doocs#3185)
Co-authored-by: Libin YANG <contact@yanglibin.info>
1 parent 26880b8 commit 91e3052

18 files changed

+857
-47
lines changed

solution/0900-0999/0973.K Closest Points to Origin/README.md

+311-15
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ tags:
7171

7272
我们将所有点按照与原点的距离从小到大排序,然后取前 $k$ 个点即可。
7373

74-
时间复杂度 $O(n \times \log n)$,空间复杂度 $O(\log n)$。其中 $n$ 为数组 `points` 的长度。
74+
时间复杂度 $O(n \times \log n)$,空间复杂度 $O(\log n)$。其中 $n$ 为数组 $\text{points}$ 的长度。
7575

7676
<!-- tabs:start -->
7777

@@ -80,7 +80,7 @@ tags:
8080
```python
8181
class Solution:
8282
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
83-
points.sort(key=lambda p: p[0] * p[0] + p[1] * p[1])
83+
points.sort(key=lambda p: hypot(p[0], p[1]))
8484
return points[:k]
8585
```
8686

@@ -89,11 +89,8 @@ class Solution:
8989
```java
9090
class Solution {
9191
public int[][] kClosest(int[][] points, int k) {
92-
Arrays.sort(points, (a, b) -> {
93-
int d1 = a[0] * a[0] + a[1] * a[1];
94-
int d2 = b[0] * b[0] + b[1] * b[1];
95-
return d1 - d2;
96-
});
92+
Arrays.sort(
93+
points, (p1, p2) -> Math.hypot(p1[0], p1[1]) - Math.hypot(p2[0], p2[1]) > 0 ? 1 : -1);
9794
return Arrays.copyOfRange(points, 0, k);
9895
}
9996
}
@@ -105,8 +102,8 @@ class Solution {
105102
class Solution {
106103
public:
107104
vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
108-
sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b) {
109-
return a[0] * a[0] + a[1] * a[1] < b[0] * b[0] + b[1] * b[1];
105+
sort(points.begin(), points.end(), [](const vector<int>& p1, const vector<int>& p2) {
106+
return hypot(p1[0], p1[1]) < hypot(p2[0], p2[1]);
110107
});
111108
return vector<vector<int>>(points.begin(), points.begin() + k);
112109
}
@@ -118,8 +115,7 @@ public:
118115
```go
119116
func kClosest(points [][]int, k int) [][]int {
120117
sort.Slice(points, func(i, j int) bool {
121-
a, b := points[i], points[j]
122-
return a[0]*a[0]+a[1]*a[1] < b[0]*b[0]+b[1]*b[1]
118+
return math.Hypot(float64(points[i][0]), float64(points[i][1])) < math.Hypot(float64(points[j][0]), float64(points[j][1]))
123119
})
124120
return points[:k]
125121
}
@@ -129,7 +125,8 @@ func kClosest(points [][]int, k int) [][]int {
129125

130126
```ts
131127
function kClosest(points: number[][], k: number): number[][] {
132-
return points.sort((a, b) => a[0] ** 2 + a[1] ** 2 - (b[0] ** 2 + b[1] ** 2)).slice(0, k);
128+
points.sort((a, b) => Math.hypot(a[0], a[1]) - Math.hypot(b[0], b[1]));
129+
return points.slice(0, k);
133130
}
134131
```
135132

@@ -138,10 +135,309 @@ function kClosest(points: number[][], k: number): number[][] {
138135
```rust
139136
impl Solution {
140137
pub fn k_closest(mut points: Vec<Vec<i32>>, k: i32) -> Vec<Vec<i32>> {
141-
points
142-
.sort_unstable_by(|a, b| (a[0].pow(2) + a[1].pow(2)).cmp(&(b[0].pow(2) + b[1].pow(2))));
143-
points[0..k as usize].to_vec()
138+
points.sort_by(|a, b| {
139+
let dist_a = f64::hypot(a[0] as f64, a[1] as f64);
140+
let dist_b = f64::hypot(b[0] as f64, b[1] as f64);
141+
dist_a.partial_cmp(&dist_b).unwrap()
142+
});
143+
points.into_iter().take(k as usize).collect()
144+
}
145+
}
146+
```
147+
148+
<!-- tabs:end -->
149+
150+
<!-- solution:end -->
151+
152+
<!-- solution:start -->
153+
154+
### 方法二:优先队列(大根堆)
155+
156+
我们可以使用一个优先队列(大根堆)来维护距离原点最近的 $k$ 个点。
157+
158+
时间复杂度 $O(n \times \log k)$,空间复杂度 $O(k)$。其中 $n$ 为数组 $\text{points}$ 的长度。
159+
160+
<!-- tabs:start -->
161+
162+
#### Python3
163+
164+
```python
165+
class Solution:
166+
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
167+
max_q = []
168+
for i, (x, y) in enumerate(points):
169+
dist = math.hypot(x, y)
170+
heappush(max_q, (-dist, i))
171+
if len(max_q) > k:
172+
heappop(max_q)
173+
return [points[i] for _, i in max_q]
174+
```
175+
176+
#### Java
177+
178+
```java
179+
class Solution {
180+
public int[][] kClosest(int[][] points, int k) {
181+
PriorityQueue<int[]> maxQ = new PriorityQueue<>((a, b) -> b[0] - a[0]);
182+
for (int i = 0; i < points.length; ++i) {
183+
int x = points[i][0], y = points[i][1];
184+
maxQ.offer(new int[] {x * x + y * y, i});
185+
if (maxQ.size() > k) {
186+
maxQ.poll();
187+
}
188+
}
189+
int[][] ans = new int[k][2];
190+
for (int i = 0; i < k; ++i) {
191+
ans[i] = points[maxQ.poll()[1]];
192+
}
193+
return ans;
194+
}
195+
}
196+
```
197+
198+
#### C++
199+
200+
```cpp
201+
class Solution {
202+
public:
203+
vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
204+
priority_queue<pair<double, int>> pq;
205+
for (int i = 0, n = points.size(); i < n; ++i) {
206+
double dist = hypot(points[i][0], points[i][1]);
207+
pq.push({dist, i});
208+
if (pq.size() > k) {
209+
pq.pop();
210+
}
211+
}
212+
vector<vector<int>> ans;
213+
while (!pq.empty()) {
214+
ans.push_back(points[pq.top().second]);
215+
pq.pop();
216+
}
217+
return ans;
218+
}
219+
};
220+
```
221+
222+
#### Go
223+
224+
```go
225+
func kClosest(points [][]int, k int) [][]int {
226+
maxQ := hp{}
227+
for i, p := range points {
228+
dist := math.Hypot(float64(p[0]), float64(p[1]))
229+
heap.Push(&maxQ, pair{dist, i})
230+
if len(maxQ) > k {
231+
heap.Pop(&maxQ)
232+
}
233+
}
234+
ans := make([][]int, k)
235+
for i, p := range maxQ {
236+
ans[i] = points[p.i]
237+
}
238+
return ans
239+
}
240+
241+
type pair struct {
242+
dist float64
243+
i int
244+
}
245+
246+
type hp []pair
247+
248+
func (h hp) Len() int { return len(h) }
249+
func (h hp) Less(i, j int) bool {
250+
a, b := h[i], h[j]
251+
return a.dist > b.dist
252+
}
253+
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
254+
func (h *hp) Push(v any) { *h = append(*h, v.(pair)) }
255+
func (h *hp) Pop() any { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }
256+
```
257+
258+
#### TypeScript
259+
260+
```ts
261+
function kClosest(points: number[][], k: number): number[][] {
262+
const maxQ = new MaxPriorityQueue();
263+
for (const [x, y] of points) {
264+
const dist = x * x + y * y;
265+
maxQ.enqueue([x, y], dist);
266+
if (maxQ.size() > k) {
267+
maxQ.dequeue();
268+
}
269+
}
270+
return maxQ.toArray().map(item => item.element);
271+
}
272+
```
273+
274+
<!-- tabs:end -->
275+
276+
<!-- solution:end -->
277+
278+
<!-- solution:start -->
279+
280+
### 方法三:二分查找
281+
282+
我们注意到,随着距离的增大,点的数量是递增的。这存在一个临界值,使得在这个值之前的点的数量小于等于 $k$,而在这个值之后的点的数量大于 $k$。
283+
284+
因此,我们可以使用二分查找,枚举距离。每一次二分查找,我们统计出距离小于等于当前距离的点的数量,如果数量大于等于 $k$,则说明临界值在左侧,我们令右边界等于当前距离;否则,临界值在右侧,我们令左边界等于当前距禽加一。
285+
286+
二分查找结束后,我们只需要返回距离小于等于左边界的点即可。
287+
288+
时间复杂度 $O(n \times \log M)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\text{points}$ 的长度,而 $M$ 为距离的最大值。
289+
290+
<!-- tabs:start -->
291+
292+
#### Python3
293+
294+
```python
295+
class Solution:
296+
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
297+
dist = [x * x + y * y for x, y in points]
298+
l, r = 0, max(dist)
299+
while l < r:
300+
mid = (l + r) >> 1
301+
cnt = sum(d <= mid for d in dist)
302+
if cnt >= k:
303+
r = mid
304+
else:
305+
l = mid + 1
306+
return [points[i] for i, d in enumerate(dist) if d <= l]
307+
```
308+
309+
#### Java
310+
311+
```java
312+
class Solution {
313+
public int[][] kClosest(int[][] points, int k) {
314+
int n = points.length;
315+
int[] dist = new int[n];
316+
int r = 0;
317+
for (int i = 0; i < n; ++i) {
318+
int x = points[i][0], y = points[i][1];
319+
dist[i] = x * x + y * y;
320+
r = Math.max(r, dist[i]);
321+
}
322+
int l = 0;
323+
while (l < r) {
324+
int mid = (l + r) >> 1;
325+
int cnt = 0;
326+
for (int d : dist) {
327+
if (d <= mid) {
328+
++cnt;
329+
}
330+
}
331+
if (cnt >= k) {
332+
r = mid;
333+
} else {
334+
l = mid + 1;
335+
}
336+
}
337+
int[][] ans = new int[k][0];
338+
for (int i = 0, j = 0; i < n; ++i) {
339+
if (dist[i] <= l) {
340+
ans[j++] = points[i];
341+
}
342+
}
343+
return ans;
344+
}
345+
}
346+
```
347+
348+
#### C++
349+
350+
```cpp
351+
class Solution {
352+
public:
353+
vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
354+
int n = points.size();
355+
int dist[n];
356+
int r = 0;
357+
for (int i = 0; i < n; ++i) {
358+
int x = points[i][0], y = points[i][1];
359+
dist[i] = x * x + y * y;
360+
r = max(r, dist[i]);
361+
}
362+
int l = 0;
363+
while (l < r) {
364+
int mid = (l + r) >> 1;
365+
int cnt = 0;
366+
for (int d : dist) {
367+
cnt += d <= mid;
368+
}
369+
if (cnt >= k) {
370+
r = mid;
371+
} else {
372+
l = mid + 1;
373+
}
374+
}
375+
vector<vector<int>> ans;
376+
for (int i = 0; i < n; ++i) {
377+
if (dist[i] <= l) {
378+
ans.emplace_back(points[i]);
379+
}
380+
}
381+
return ans;
382+
}
383+
};
384+
```
385+
386+
#### Go
387+
388+
```go
389+
func kClosest(points [][]int, k int) (ans [][]int) {
390+
n := len(points)
391+
dist := make([]int, n)
392+
l, r := 0, 0
393+
for i, p := range points {
394+
dist[i] = p[0]*p[0] + p[1]*p[1]
395+
r = max(r, dist[i])
396+
}
397+
for l < r {
398+
mid := (l + r) >> 1
399+
cnt := 0
400+
for _, d := range dist {
401+
if d <= mid {
402+
cnt++
403+
}
404+
}
405+
if cnt >= k {
406+
r = mid
407+
} else {
408+
l = mid + 1
409+
}
410+
}
411+
for i, p := range points {
412+
if dist[i] <= l {
413+
ans = append(ans, p)
414+
}
415+
}
416+
return
417+
}
418+
```
419+
420+
#### TypeScript
421+
422+
```ts
423+
function kClosest(points: number[][], k: number): number[][] {
424+
const dist = points.map(([x, y]) => x * x + y * y);
425+
let [l, r] = [0, Math.max(...dist)];
426+
while (l < r) {
427+
const mid = (l + r) >> 1;
428+
let cnt = 0;
429+
for (const d of dist) {
430+
if (d <= mid) {
431+
++cnt;
432+
}
433+
}
434+
if (cnt >= k) {
435+
r = mid;
436+
} else {
437+
l = mid + 1;
438+
}
144439
}
440+
return points.filter((_, i) => dist[i] <= l);
145441
}
146442
```
147443

0 commit comments

Comments
 (0)