常見題:無序數組中第k大的數(python)

思路一:暴力解法

先對數組排序,然後找出第k個位置

sorted(nums)[-k]

算法的時間複雜度爲 O(N log N),空間複雜度爲 O(1)

思路二:利用快排思想

(https://blog.csdn.net/wenqiwenqi123/article/details/81669899)
快速排序每次把一個元素交換到正確的位置,同時把左邊的都放上大的,右邊都放上小的。這個算法每一次選取一個樞紐元,排序之後,查看樞紐元的位置。如果它的位置大於K,就說明,要求出前面一個子序列的第K大的元素。反之,如果小於K,就說明要求出在後面一個序列的第K - 前一個序列的長度個元素。

如此,就把這個問題改變成了一個可以用快排思想解決的問題。對於快速排序,算法複雜度是O(N*logN)。而這個算法的算法複雜度是O(N)。爲什麼呢?

其實這個地方的算法複雜度分析很有意思。第一次交換,算法複雜度爲O(N),接下來的過程和快速排序不同,快速排序是要繼續處理兩邊的數據,再合併,合併操作的算法複雜度是O(1),於是總的算法複雜度是O(N*logN)(可以這麼理解,每次交換用了N,一共logN次)。但是這裏在確定樞紐元的相對位置(在K的左邊或者右邊)之後不用再對剩下的一半進行處理。也就是說第二次插入的算法複雜度不再是O(N)而是O(N/2),這不還是一樣嗎?其實不一樣,因爲接下來的過程是1+1/2+1/4+… < 2,換句話說就是一共是O(2N)的算法複雜度也就是O(N)的算法複雜度。

 
def quicksort(num ,low ,high):  #快速排序
    if low< high:
        location = partition(num, low, high)
        quicksort(num, low, location - 1)
        quicksort(num, location + 1, high)
 
def partition(num, low, high):
    pivot = num[low]
    while (low < high):
        while (low < high and num[high] > pivot):
            high -= 1
        while (low < high and num[low] < pivot):
            low += 1
        temp = num[low]
        num[low] = num[high]
        num[high] = temp
    num[low] = pivot
    return low
 
def findkth(num,low,high,k):   #找到數組裏第k個數
        index=partition(num,low,high)
        if index==k:return num[index]
        if index<k:
            return findkth(num,index+1,high,k)
        else:
            return findkth(num,low,index-1,k)
 
 
pai = [2,3,1,5,4,6]
# quicksort(pai, 0, len(pai) - 1)
 
print(findkth(pai,0,len(pai)-1,0))

思路三:插入排序

由於是要找 k 個最大的數,所以沒有必要對所有數進行完整的排序。每次只保留 k 個當前最大的數就可以,然後每次對新來的元素跟當前 k 個樹中最小的數比較,新元素大的話則插入到數組中,否則跳過。循環結束後數組中最小的數即是我們要找到第 k 大的數。 時間複雜度 (n-k)logk

def Find_Kth_max(array,k):
    for i in range(1,k):
        for j in range(i,0,-1):
            if array[j] > array[j-1]:
                array[j],array[j-1] = array[j-1],array[j]
            else:
                pass
    for i in range(k,len(array)):
        if array[i] > array[k-1]:
            array[k-1] = array[i]
            for j in range(k-1,0,-1):
                if array[j] > array[j-1]:
                    array[j],array[j-1] = array[j-1],array[j]
                else:
                    pass
    return array[k-1]
            
print(Find_Kth_max([2,1,4,3,5,9,8,0,1,3,2,5],3))  

思路四:堆排序

維護一個k大小的最小堆,對於數組中的每一個元素判斷與堆頂的大小,若堆頂較大,則不管,否則,彈出堆頂,將當前值插入到堆中,繼續調整最小堆。時間複雜度O(n * logk)

第一種:利用庫函數

class Solution:
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        return heapq.nlargest(k, nums)[-1]

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        heap = []
        for x in nums:
            heapq.heappush(heap, x)
            if len(heap) > k: 
                heapq.heappop(heap)
        return heapq.heappop(heap) # [5,6]  從堆中彈出最小的元素

第二種:手動建堆

def heap_build(parent,heap):
    child = 2*parent+1
    while child<len(heap):
        if child+1<len(heap) and heap[child+1]<heap[child]:
            child = child+1
        if heap[parent]<= heap[child]:
            break
        heap[parent],heap[child] = heap[child],heap[parent]
        parent,child = child,2*child+1
    return heap
    
def Find_heap_kth(array,k):
    if k > len(array):
        return None
    heap = array[:k]
    for i in range(k,-1,-1):
        heap_build(i,heap)
    for j in range(k,len(array)):
        if array[j]>heap[0]:
            heap[0] = array[j]
            heap_build(0,heap)
    return heap[0]
        
            
print(Find_heap_kth([2,1,4,3,5,9,8,0,1,3,2,5],6)) 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章