詳解快速排序


冒泡排序是在每一輪中只把1個元素冒泡到數列的一段,而快速排序則在每一輪挑選一個基准元素,並讓其他比他大的元素移動到數列一邊,比他小的元素移動到數列的另一邊。從而不斷地進行着在那個已經被分出來地一邊和另一邊中進行着上述地操作,直至都為一個元素,然后再把他們組合起來就成了個有序地數列。這就是分治法地思想。那么他的時間復雜度是O(nlogn),原因是,他需要把數組中的全部元素都需要遍歷一遍,時間復雜度是O(n),這樣一共需要遍歷幾遍呢?假設元素個數是n,那么平均情況下需要logn輪(一般在程序上一分為2的操作都是logn)
這里主要地問題是
基准元素地選擇,基准元素一般選擇第一個即可,但數列的第一個若是最小值或者最大值,這樣數列就無法一分為二了,時間復雜度就退化成了O(n).
解決方案是
我們可以隨機選取一個元素作為基准元素,並且讓基准元素和數列首元素交換位置。這樣就有很大概率避免基准元素是數列的最大值或最小值了。

總結

所以快速排序的平均時間復雜度是O(nlogn),最壞情況下的時間復雜度是O(n*n)
這里采用雙邊循環法和單邊循環法來實現快速排序;當然這里均使用了遞歸

雙邊循環法

文字描述(待補)


def quick_sort(start_index, end_index, array=[]):
    # 遞歸結束條件:startIndex大等於endIndex的時候
    if start_index >= end_index:
        return
    # 得到基准元素位置
    pivot_index = partition_v1(start_index, end_index, array)
    # 根據基准元素,分成兩部分遞歸排序
    quick_sort(start_index, pivot_index - 1, array)
    quick_sort(pivot_index + 1, end_index, array)


def partition_v1(start_index, end_index, array=[]):
    # 取第一個位置的元素作為基准元素(也可以選擇隨機位置)
    pivot = array[start_index]
    left = start_index
    right = end_index
    while left != right:
        # 控制right指針比較並左移
        while left < right and array[right] > pivot:
            right -= 1
        # 控制left指針比較並右移
        while left < right and array[left] <= pivot:
            left += 1
        # 交換left和right指向的元素
        if left < right:
            p = array[left]
            array[left] = array[right]
            array[right] = p
    # pivot和指針重合點交換
    array[start_index] = array[left]
    array[left] = pivot
    return left

單邊循環法

單邊循環法實現(待補)

def quick_sort(start_index, end_index, array=[]):
    # 遞歸結束條件:startIndex大等於endIndex的時候
    if start_index >= end_index:
        return
    # 得到基准元素位置
    pivot_index = partition_v1(start_index, end_index, array)
    # 根據基准元素,分成兩部分遞歸排序
    quick_sort(start_index, pivot_index - 1, array)
    quick_sort(pivot_index + 1, end_index, array)

def partition_v2(start_index, end_index, array=[]):
    # 取第一個位置的元素作為基准元素(也可以選擇隨機位置)
    pivot = array[start_index]
    mark = start_index
    for i in range(start_index+1, end_index+1):
        if array[i] < pivot:
            mark += 1
            p = array[mark]
            array[mark] = array[i]
            array[i] = p
    array[start_index] = array[mark]
    array[mark] = pivot
    return mark

采用非遞歸來實現

遞歸:
代碼中一層一層的方法調用,本身就使用了一個方法調用棧。每次進入一個新方法,相當於入棧;每次有方法返回,就相當於出棧。
遞歸四要素:返回值,函數的分拆,終止條件,參數
非遞歸文字實現(待補)


def quick_sort(start_index, end_index, array=[]):
        # 用一個集合棧來代替遞歸的函數棧
        quick_sort_stack = []
        # 整個數列的起止下標,以哈希的形式入棧
        root_param = {"startIndex": start_index, "endIndex": end_index}
        quick_sort_stack.append(root_param)

        # 循環結束條件:棧為空時結束
        while len(quick_sort_stack) > 0:
            # 棧頂元素出棧,得到起止下標
            param = quick_sort_stack.pop()
            # 得到基准元素位置
            pivot_index = partition(param.get("startIndex"), param.get("endIndex"), array)
            # 根據基准元素分成兩部分, 把每一部分的起止下標入棧
            if param.get("startIndex") < pivot_index - 1:
                left_param = {"startIndex": param.get("startIndex"), "endIndex": pivot_index - 1}
                quick_sort_stack.append(left_param)
            if pivot_index + 1 < param.get("endIndex"):
                right_param = {"startIndex": pivot_index + 1, "endIndex": param.get("endIndex")}
                quick_sort_stack.append(right_param)


def partition(start_index, end_index, array=[]):
    # 取第一個位置的元素作為基准元素(也可以選擇隨機位置)
    pivot = array[start_index]
    mark = start_index
    for i in range(start_index+1, end_index+1):
        if array[i] < pivot:
            mark += 1
            p = array[mark]
            array[mark] = array[i]
            array[i] = p
    array[start_index] = array[mark]
    array[mark] = pivot
    return mark


my_array = list([3, 4, 14, 1, 5, 6, 7, 8, 1, -1, 0, 9, 11])
quick_sort(0, len(my_array)-1, my_array)
print(my_array)



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM