題目
輸入整數數組 arr ,找出其中最小的 k 個數。例如,輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。
示例 1:
輸入:arr = [3,2,1], k = 2
輸出:[1,2] 或者 [2,1]
示例 2:
輸入:arr = [0,1,2,1], k = 1
輸出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
解法一(查找排序)
思路:始終維持一個從小到大排列的有序列表,每次跟最後一個值(最大值)進行對比,如果比最大值小則替換,然後重新對列表排序保持有序
- 結果列表未滿之前,一直填入直到填滿
- 每次跟最後的值進行比較,如果比列表中值小,則替換
- 每次操作對列表,都重新對列表進行排序
- 時間複雜度:O(N2logN)
- 空間複雜度:O(1)
class Solution:
# author: [email protected]
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k<= 0: #長度異常
return []
if k>=len(arr):
return arr;
idx = 0
rst = list()
for idx in range(0,k):#長度未填滿
rst.append(arr[idx])
rst = sorted(rst)
for idx in range(k,len(arr)):
flag = False
if arr[idx] < rst[k-1]:#最後一個是最大值
rst[k-1] = arr[idx]
flag = True
if flag == True:
rst = sorted(rst)
return rst
解法二(排序截取)
思路:上面解法每次對結果排序浪費時間,當前優化通過首先對原始數據排序,然後取最小的k個。
- 排序原數組
- 提取前面k個值
- 時間複雜度:O(NlogN)
- 空間複雜度:O(N)
class Solution:
# author: [email protected]
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
rst = sorted(arr)
return rst[0:k]
解法三(大頂堆)
思路:大頂堆本身是一個從大到小排列的數據結構,因此維護一個僅有k個元素的大頂堆即可
- python中的Heapq是一個小頂堆,因此對所有值取負值來表示大頂堆
- 如果當前值大於堆頂元素,則替換
- 將堆元素再取反,得到原值
- 時間複雜度:O(NlogN)
- 空間複雜度:O(1)
class Solution:
# author: [email protected]
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k <= 0:
return []
rst = [-v for v in arr[0:k]]
heapq.heapify(rst)# 建堆
for item in arr[k:len(arr)]:
if -rst[0] > item:
heapq.heapreplace(rst,-item) #更換堆頂元素
return [-v for v in rst] #取反得到原值
解法四(快排)
思路:基於快排的思路,尋找分割座標等於k的位置,則表示左邊k個都小於右邊,兩邊內部可能依舊是無序的。
- 使用快排對列表進行初步的分割
- 如果分割點小於k,則分割點在右邊,繼續對右邊快排,大於k則在左邊
- 如果因爲left>=right退出,說明k大於等於列表長度,因爲left和right始終在向k方向移動
- 時間複雜度:O(n),看起來是3層while循環,但數據只遍歷了一遍
- 空間複雜度:O(1)
class Solution:
# author: [email protected]
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
idx,left,right = 0,0,len(arr)-1
while left<right and idx != k:
val = arr[left]
i,j = left,right
while i < j: # 快排邏輯
while i < j and arr[j] >= val:# 從右向左找小值
j -= 1
arr[i] = arr[j]
while i < j and arr[i] <= val:# 從左向右找大值
i += 1
arr[j] = arr[i]
idx, arr[i] = i, val
if idx < k: # 分界值在右邊
left = idx + 1
elif idx > k: # k在左邊
right = idx - 1
return arr[0:k]