4種解法 - 最小的k個數


題目

輸入整數數組 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


解法一(查找排序)

思路:始終維持一個從小到大排列的有序列表,每次跟最後一個值(最大值)進行對比,如果比最大值小則替換,然後重新對列表排序保持有序

  1. 結果列表未滿之前,一直填入直到填滿
  2. 每次跟最後的值進行比較,如果比列表中值小,則替換
  3. 每次操作對列表,都重新對列表進行排序
  • 時間複雜度: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個。

  1. 排序原數組
  2. 提取前面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個元素的大頂堆即可

  1. python中的Heapq是一個小頂堆,因此對所有值取負值來表示大頂堆
  2. 如果當前值大於堆頂元素,則替換
  3. 將堆元素再取反,得到原值
  • 時間複雜度: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個都小於右邊,兩邊內部可能依舊是無序的。

  1. 使用快排對列表進行初步的分割
  2. 如果分割點小於k,則分割點在右邊,繼續對右邊快排,大於k則在左邊
  3. 如果因爲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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章