# **Leetcode筆記(K Closest Points to Origin)**
:::info
:information_source: 題目 : K Closest Points to Origin, 類型 : heap , 等級 : medium
日期 : 2023/06/07,2023/07/08,2024/02/15
:::
### 嘗試
2023/07/08
- 時間複雜度:
- 形成list過程的時間複雜度為 O(N),其中 N 是 `points` 列表的長度。
- 不斷從 `maxHeap` 中彈出最大的元素,直到堆中的元素個數等於 k。每次彈出操作的時間複雜度為 O(log N),並操作(N - k)次。
- 存儲在 `res` 列表中。時間複雜度為 O(k)。
- 因此,整個過程的時間複雜度為 O(N + (N - k) log N)。
- 空間複雜度:
- 在 `maxHeap` 需要的額外空間是 O(N)。
- `res` 列表的大小是 k,所以需要的額外空間是 O(k)。
```python
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
maxHeap = []
for x, y in points:
dis = - (x ** 2 + y ** 2)
maxHeap.append([dis, x, y])
heapq.heapify(maxHeap)
while len(maxHeap) > k:
heapq.heappop(maxHeap)
res = [[x, y] for dis, x, y in maxHeap]
return res
```
使用了quick select
- 時間複雜度:
- 在 `quickSelect` 方法中,通過呼叫 `partition` 方法將陣列切割為兩部分,這個過程的時間複雜度為 O(N),其中 N 是陣列的長度。
- 在最壞情況下,**`quickSelect`** 方法的時間複雜度為 O(N^2),但**平均情況下的時間複雜度為 O(N)**。
- **快速選擇並不遞歸訪問雙邊,而是只遞歸進入一邊的元素中繼續尋找。這降低了平均時間複雜度,從O(n log n)至O(n)**
- 最後返回 `points[:k]`,需要 O(k) 的時間複雜度。
- 綜合起來,整個演算法的時間複雜度為 O(N + k)。
- 空間複雜度:
- 演算法僅使用了常數級別的額外空間,沒有使用額外的資料結構。
- 因此,空間複雜度為 O(1)。
```python
from typing import List
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
self.quickSelect(points, 0, len(points) - 1, k)
return points[:k]
def quickSelect(self, points: List[List[int]], start: int, end: int, k: int) -> None:
if start >= end: # 代表list為空或只剩一個元素
return
# 基準點
pivot_index = self.partition(points, start, end)
if pivot_index == k:
return
elif pivot_index < k:
self.quickSelect(points, pivot_index + 1, end, k)
else:
self.quickSelect(points, start, pivot_index - 1, k)
# 最後數組會是 : 小於pivot的在左 大於pivot的在右
def partition(self, points: List[List[int]], start: int, end: int) -> int:
pivot = points[end] # 選定一數為pivot
i = start - 1
for j in range(start, end): # 遍歷這個區間
if self.compare(points[j], pivot) <= 0: # 代表當前元素points[j] 小於pivot
i += 1
points[i], points[j] = points[j], points[i] # 比較小的被放到i上
# 如果當前元素points[j] 大於pivot 則甚麼都不發生
points[i+1], points[end] = points[end], points[i+1] # 最後要把end移到中間 當作分界
return i + 1 # 回傳被分的中界點
def compare(self, point1: List[int], point2: List[int]) -> int:
dist1 = point1[0]**2 + point1[1]**2
dist2 = point2[0]**2 + point2[1]**2
return dist1 - dist2
# dist1 < dist2 -> 回傳負數
# dist1 = dist2 -> 回傳0
# dist1 > dist2 -> 回傳正數
```
2024/02/15
```python
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
minHeap = []
for x, y in points:
minHeap.append((self.distance(x, y), x, y))
heapq.heapify(minHeap)
res = []
while k:
dist, x, y = heapq.heappop(minHeap)
res.append([x, y])
k -= 1
return res
def distance(self, x, y):
return (x ** 2 + y ** 2) ** (1/2)
```
---
### **優化**
[這篇](https://mp.weixin.qq.com/s/FFsvWXiaZK96PtUg-mmtEw)透徹解說topK
時間複雜度:
- 將每個點的距離和坐標存入最小堆的過程,時間複雜度為O(nlogn),其中n是points的長度。
- 從最小堆中取出k個最小距離的點的過程,時間複雜度為O(klogn)。
總時間複雜度為O(nlogn + klogn) = O((n+k)logn)。
空間複雜度:
- 儲存最小堆的空間複雜度為O(n)。
- 儲存結果的空間複雜度為O(k)。
總空間複雜度為O(n + k)。
```python
class Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
minHeap = []
for r, c in points:
dist = r **2 + c **2
minHeap.append((dist, r, c))
# 由第一個元素排列
heapq.heapify(minHeap) # 轉成heap
res = []
while k > 0:
dist, r, c = heapq.heappop(minHeap)
res.append([r, c])
k -= 1
return res
```
---
**:warning: 錯誤語法**
:::warning
:::
**:thumbsup:學習**
:::success
:::
**思路**
**講解連結**
Provided by. 58沈剑
###### tags: `heap` `medium` `leetcode`