算法思考-topk問題堆排序、快速排序比較

題目:分析求解topk(big)問題時 堆排序快速排序 的使用場景


快速排序求解

       這種算法利用了快速排序的主要思想:每次調用快速排序的partition函數後將會得出一個數n的最終位置,比較n及其右邊所有數的總個數與目標個數k

若大於k,則對數n右邊的數構成的數組調用partition函數;
若等於k,則說明n及其右邊的數就是我們想要得到的那k個數,排序結束;
若小於k,這說明最終的k個數中還包含了n左邊的數,對n左邊的數構成的數組調用partition函數。

Pyhton代碼:

import numpy as np

def qselect(A, k):
    if len(A) < k:
        return A
    pivot = A[-1]
    tmp = A[:-1][A[:-1] >= [pivot]]
    right = np.insert(tmp, 0, pivot)
    try:
        rlen = len(right)
    except:
        rlen = 1
    if rlen == k:
        return right
    if rlen > k:
        return qselect(right, k)
    else:
        left = [x for x in A[:-1] if x < pivot]
        return np.append(qselect(left, k-rlen), right)

L = np.random.randint(0, 10000, size=10000)
k = 100
qselect(L, k)

       利用快速排序解決topk問題時,速度非常快,但是缺點它在數組中直接交換數據,所以要用一個數組來存儲所有的數據,當數據量十分龐大時將佔用驚人的內存,屬於用空間換時間的算法。

堆排序

       這種算法的大致思路爲:首先初始化一個大小爲k的小根堆,然後遍歷數組,每遍歷一個數就將其與堆的根節點相比較,若比根節點大,則將其與根節點交換變更新堆結構。當數組遍歷完成時堆中存儲的就是我們想要的結果。

Pyhton代碼:

import numpy as np

def sift(l, low, hight):
    # 調整堆結構
    tmp = l[low]
    i = low
    j = 2 * i + 1
    while j <= hight:  # 情況2:i已經是最後一層
        if j + 1 <= hight and l[j + 1] < l[j]:  # 右孩子存在並且小於左孩子
            j += 1
        if tmp > l[j]:
            l[i] = l[j]
            i = j
            j = 2 * i + 1
        else:
            break  # 情況1:j位置比tmp小
    l[i] = tmp

def top_k(l, k):
    heap = l[:k]
    # 建堆
    for i in range(k // 2 - 1, -1, -1):
        sift(heap, i, k - 1)
    for i in range(k, len(l)):
        if l[i] > heap[0]:
            heap[0] = l[i]
            sift(heap, 0, k - 1)

    for i in range(k - 1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i - 1)
    return heap

L = np.random.randint(0, 10000, size=10000)
k = 100
top_k(L, k)

       利用堆排序解決topk問題時,速度是所有算法中除了快速排序之外最快的,它的突出優點就是不論數據量有多大,它的空間複雜度總是常數級的,在處理大數據時常將數據分塊打包後分別利用堆排序進行篩選,再將篩選結果進行比較,選出我們想要的最終結果。

兩種算法直觀比較:

在這裏插入圖片描述

縱座標爲耗時,橫座標爲初始序列長度,數值取值範圍爲[0,999999999],k(即最終篩選出的數值個數)爲100,