Python實現(xiàn)七種常用排序(冒泡挠将、快排胳岂、歸并编整、選擇舔稀、堆排序、插入掌测、希爾)

各個分類的代表算法
排序方法 平均情況 最好情況 最壞情況 輔助空間 穩(wěn)定性
冒泡排序 O(n^2) O(n) O(n^2) O(1) 穩(wěn)定
選擇排序 O(n^2) O(n^2) O(n^2) O(1) 穩(wěn)定
插入排序 O(n^2) O(n) O(n^2) O(1) 穩(wěn)定
希爾排序 O(n^2) O(n^1.3) O(n^2) O(1) 不穩(wěn)定
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不穩(wěn)定
歸并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 穩(wěn)定
快速排序 O(nlogn) O(nlogn) O(n^2) O(logn) ~ O(n) 不穩(wěn)定

1. 冒泡排序

# encoding:utf8
def BubbleSort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(n - 1, i, -1):
            if arr[j - 1] > arr[j]:
                arr[j], arr[j - 1] = arr[j - 1], arr[j]
    return arr


# 冒泡排序優(yōu)化:用Flag避免數(shù)組已經(jīng)有序的情況
def BubbleSort2(arr):
    n = len(arr)
    Flag = True
    for i in range(n):
        if not Flag:
            break
        Flag = False
        for j in range(n - 1, i, -1):
            if arr[j - 1] > arr[j]:
                arr[j], arr[j - 1] = arr[j - 1], arr[j]
                Flag = True
    return arr


# 最優(yōu)時間復(fù)雜度 O(n^2)内贮, 最差時間復(fù)雜度 O(n)
if __name__ == "__main__":
    arr = [6, 3, 2, 5, 1, 6, 3]
    print(BubbleSort(arr))

2.1 快速排序

# encoding:utf8

def partition(arr, low, high):
    # 用數(shù)組中的第一個記錄作為樞紐記錄
    privotKey = arr[low]
    # 從數(shù)組的兩端交替向中間掃描
    while low < high:
        while low < high and arr[high] >= privotKey:
            high -= 1
        # 將比樞紐小的數(shù)交換到低端
        arr[low], arr[high] = arr[high], arr[low]

        while low < high and arr[low] <= privotKey:
            low += 1
        # 將比樞紐大的數(shù)交換到高端
        arr[low], arr[high] = arr[high], arr[low]
    # 返回樞紐所在位置
    return low


def QuickSort(arr, low, high):
    if low < high:
        # 算出樞紐值,將數(shù)組一分為二
        privot = partition(arr, low, high)
        # 對低子表遞歸排序
        QuickSort(arr, low, privot - 1)
        # 對高子表遞歸排序
        QuickSort(arr, privot + 1, high)


# 最優(yōu)時間復(fù)雜度 O(nlogn)汞斧, 最差時間復(fù)雜度 O(n^2)
# 最優(yōu)空間復(fù)雜度 O(logn)夜郁, 最差空間復(fù)雜度 O(n)
if __name__ == "__main__":
    arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 24]
    QuickSort(arr, 0, len(arr) - 1)
    print(arr)

2.2 快速排序優(yōu)化1

# encoding:utf8

# 優(yōu)化選取樞紐,選取的樞紐值決定交換次數(shù)粘勒,我們盡量將樞紐值取到數(shù)組的中間位置
# 選擇三數(shù)取中竞端,即取三個關(guān)鍵字先進(jìn)行排序,將中間數(shù)作為樞紐庙睡。
def partition(arr, low, high):
    mid = low + (high - low) // 2
    # 交換左端與右端事富,保證左端較小
    if arr[low] > arr[high]:
        arr[low], arr[high] = arr[high], arr[low]
    # 交換中間與右端數(shù)據(jù),保證中間較小
    if arr[mid] > arr[high]:
        arr[mid], arr[high] = arr[high], arr[mid]
    # 交換中間與左端數(shù)據(jù)乘陪,保證左端較小
    if arr[mid] > arr[low]:
        arr[mid], arr[low] = arr[low], arr[mid]
    # 此時low是三個數(shù)中的中間值

    pivotKey = arr[low]
    while low < high:
        if low < high and arr[high] >= pivotKey:
            high -= 1
        arr[low], arr[high] = arr[high], arr[low]

        if low < high and arr[low] <= pivotKey:
            low += 1
        arr[low], arr[high] = arr[high], arr[low]

    return low


def QuickSort(arr, low, high):
    if low < high:
        pivot = partition(arr, low, high)
        QuickSort(arr, low, pivot - 1)
        QuickSort(arr, pivot + 1, high)


arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 24]
QuickSort(arr, 0, len(arr) - 1)
print(arr)

2.2 快速排序優(yōu)化2

# encoding:utf8

# 優(yōu)化不必要的交換
def partition(arr, low, high):
    # 三數(shù)交換
    mid = low + (high - low) // 2
    if arr[low] > arr[high]:
        arr[low], arr[high] = arr[high], arr[low]
    if arr[mid] > arr[high]:
        arr[mid], arr[high] = arr[high], arr[mid]
    if arr[mid] > arr[low]:
        arr[mid], arr[low] = arr[low], arr[mid]

    pivotKey = arr[low]
    # 設(shè)置一個哨兵元素
    pre = pivotKey
    while low < high:
        while low < high and arr[high] >= pivotKey:
            high -= 1
        # 采用替換而不是交換
        arr[low] = arr[high]

        while low < high and arr[low] <= pivotKey:
            low += 1
        arr[high] = arr[low]
        arr[low] = pre

    return low


def QuickSort(arr, low, high):
    if low < high:
        pivot = partition(arr, low, high)
        QuickSort(arr, low, pivot - 1)
        QuickSort(arr, pivot + 1, high)


arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 2]
QuickSort(arr, 0, len(arr) - 1)
print(arr)

2.3 快速排序優(yōu)化3

# encoding:utf8

# 優(yōu)化遞歸
def partition(arr, low, high):
    # 三數(shù)取中
    mid = low + (high - low) // 2
    if arr[low] > arr[high]:
        arr[low], arr[high] = arr[high], arr[low]
    if arr[mid] > arr[high]:
        arr[mid], arr[high] = arr[high], arr[mid]
    if arr[mid] > arr[low]:
        arr[mid], arr[low] = arr[low], arr[mid]

    pivotKey = arr[low]
    # 設(shè)置哨兵元素
    pre = pivotKey

    while low < high:
        while low < high and arr[high] >= pivotKey:
            high -= 1
        arr[low] = arr[high]

        while low < high and arr[low] <= pivotKey:
            low += 1
        arr[high] = arr[low]
        arr[low] = pre

    return low

def QuickSort(arr, low, high):
    while low < high:
        pivot = partition(arr, low, high)
        # 對低子表遞歸排序
        QuickSort(arr, low, pivot - 1)
        # 尾遞歸
        low = pivot + 1

arr = [10, 7, 8, 9, 1, 5, 2, 4, 6, 13, 4]
QuickSort(arr, 0, len(arr) - 1)
print(arr)

3. 歸并排序

# encoding:utf8

def merge(s1, s2, s):
    """將兩個列表是s1统台,s2按順序融合為一個列表s,s為原列表"""
    # j和i就相當(dāng)于兩個指向的位置,i指s1啡邑,j指s2
    i = j = 0
    while i + j < len(s):
        # j == len(s2)時說明s2走完了贱勃,或者s1沒走完并且s1中該位置是最小的
        if j == len(s2) or (i < len(s1) and s1[i] < s2[j]):
            s[i + j] = s1[i]
            i += 1
        else:
            s[i + j] = s2[j]
            j += 1


def MergeSort(arr):
    n = len(arr)
    # 剩一個或沒有直接返回,不用排序
    if n <= 1:
        return arr
    # 拆分
    mid = n // 2
    left = arr[:mid]
    right = arr[mid:]
    # 子序列遞歸調(diào)用排序
    MergeSort(left)
    MergeSort(right)
    # 合并
    merge(left, right, arr)


# 最優(yōu)時間復(fù)雜度 O(nlogn)
# 最優(yōu)空間復(fù)雜度 O(n + logn)
if __name__ == '__main__':
    s = [1, 7, 3, 5, 4, 2, 5, 3]
    MergeSort(s)
    print(s)

4. 歸并排序

# encoding:utf8

def merge(s1, s2, s):
    """將兩個列表是s1谤逼,s2按順序融合為一個列表s,s為原列表"""
    # j和i就相當(dāng)于兩個指向的位置贵扰,i指s1,j指s2
    i = j = 0
    while i + j < len(s):
        # j == len(s2)時說明s2走完了流部,或者s1沒走完并且s1中該位置是最小的
        if j == len(s2) or (i < len(s1) and s1[i] < s2[j]):
            s[i + j] = s1[i]
            i += 1
        else:
            s[i + j] = s2[j]
            j += 1


def MergeSort(arr):
    n = len(arr)
    # 剩一個或沒有直接返回戚绕,不用排序
    if n <= 1:
        return arr
    # 拆分
    mid = n // 2
    left = arr[:mid]
    right = arr[mid:]
    # 子序列遞歸調(diào)用排序
    MergeSort(left)
    MergeSort(right)
    # 合并
    merge(left, right, arr)


# 最優(yōu)時間復(fù)雜度 O(nlogn)
# 最優(yōu)空間復(fù)雜度 O(n + logn)
if __name__ == '__main__':
    s = [1, 7, 3, 5, 4, 2, 5, 3]
    MergeSort(s)
    print(s)

5. 簡單選擇排序

# encoding:utf8

def SelectSort(arr):
    # 記錄每一個元素的下標(biāo)
    for i in range(len(arr)):
        min = i
        for j in range(i + 1, len(arr)):
            if arr[min] > arr[j]:
                min = j
        if i != min:
            arr[i], arr[min] = arr[min], arr[i]

    return arr


# 最優(yōu)時間復(fù)雜度 O(1), 最差時間復(fù)雜度 O(n^2)
if __name__ == '__main__':
    s = [1, 7, 3, 5, 4, 2, 5, 5]
    res = SelectSort(s)
    print(res)

6. 堆排序

# encoding:utf8

from collections import deque

"""
堆排序思想:
首先將待排序的數(shù)組構(gòu)造出一個大根堆
取出這個大根堆的堆頂節(jié)點(最大值)贵涵,與堆的最下最右的元素進(jìn)行交換列肢,然后把剩下的元素再構(gòu)造出一個大根堆
重復(fù)第二步,直到這個大根堆的長度為1宾茂,此時完成排序瓷马。
"""

# 交換兩個元素
def swap(l, i, j):
    l[i], l[j] = l[j], l[i]
    return l

def swap_param(L, i, j):
    L[i], L[j] = L[j], L[i]
    return L


# 調(diào)整堆
def heap_adjust(arr, start, end):
    temp = arr[start]
    i = start
    # 沿關(guān)鍵字較大的孩子結(jié)點(2i)向下篩選
    j = 2 * i
    # 遍歷當(dāng)前節(jié)點的孩子結(jié)點
    while j <= end:
        # 如果當(dāng)前節(jié)點不是最后一個節(jié)點而且左孩子小于右孩子,記錄較大值
        if j < end and arr[j] < arr[j + 1]:
            j += 1  # j 為關(guān)鍵字中較大的記錄的下標(biāo)
        if temp >= arr[j]:
            break
        else:
            arr[i] = arr[j]
            i = j
            j = 2 * i
    arr[i] = temp  # 交換兩個數(shù)字


def heap_sort(arr):
    """
    由于堆排序是一種完全二叉樹跨晴,數(shù)組下標(biāo)是從0開始的欧聘,二叉樹的節(jié)點從1開始,
    所以這里我們引入一個輔助空間端盆,用deque追加一個輔助位
    """
    l = deque(arr)
    l.appendleft(0)
    # 引入了一個輔助空間怀骤,這里l_len的長度-1
    l_len = len(l) - 1
    # first_sort_count 是有孩子的節(jié)點
    first_sort_count = l_len // 2

    # 從后往前將序列調(diào)整為一個大根堆
    print(l)
    for i in range(first_sort_count, 0, -1):
        heap_adjust(l, i, l_len)
    print(l)

    # 從后往前將堆頂元素和堆末尾元素進(jìn)行交換费封, 然后把剩下的元素調(diào)整為一個大根堆
    for i in range(l_len, 0, -1):
        # 將堆頂記錄和當(dāng)前未經(jīng)排序的子序列的最后一個記錄交換
        l = swap_param(l, 1, i)
        # 將[1 ~ i-1]重新調(diào)整為大頂堆
        heap_adjust(l, 1, i - 1)
    # print(l)
    return [l[i] for i in range(1, len(l))]


# 時間復(fù)雜度 O(nlogn)
if __name__ == '__main__':
    s = [1, 7, 3, 5, 4, 2, 5, 9, 4, 3, 5]
    res = heap_sort(s)
    print(res)

7. 直接插入排序

# encoding:utf8

def insert_sort(arr):
    for i in range(1, len(arr)):
        # 保存當(dāng)前元素的值,依次比較選擇
        key = arr[i]
        # 從當(dāng)前記錄開始往前進(jìn)行比較
        j = i - 1

        while j >= 0 and key < arr[j]:
            arr[j + 1] = arr[j]  # 記錄后移
            j -= 1
        arr[j + 1] = key  # 插入到正確的位置

    return arr


# 時間復(fù)雜度 O(n^2)
if __name__ == '__main__':
    arr = [1, 7, 3, 5, 4, 2, 5, 9, 4, 3, 5]
    res = insert_sort(arr)
    print(res)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒋伦,一起剝皮案震驚了整個濱河市弓摘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痕届,老刑警劉巖韧献,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異研叫,居然都是意外死亡锤窑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門嚷炉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渊啰,“玉大人,你說我怎么就攤上這事申屹』嬷ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵独柑,是天一觀的道長迈窟。 經(jīng)常有香客問我,道長忌栅,這世上最難降的妖魔是什么车酣? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮索绪,結(jié)果婚禮上湖员,老公的妹妹穿的比我還像新娘。我一直安慰自己瑞驱,他們只是感情好娘摔,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唤反,像睡著了一般凳寺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彤侍,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天肠缨,我揣著相機與錄音,去河邊找鬼盏阶。 笑死晒奕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脑慧,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼魄眉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闷袒?” 一聲冷哼從身側(cè)響起坑律,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霜运,沒想到半個月后脾歇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蒋腮,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡淘捡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年池摧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膘魄。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡绢慢,死狀恐怖胰舆,靈堂內(nèi)的尸體忽然破棺而出缚窿,到底是詐尸還是另有隱情倦零,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站欣尼,受9級特大地震影響愕鼓,放射性物質(zhì)發(fā)生泄漏菇晃。R本人自食惡果不足惜磺送,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一崇呵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧犹褒,春花似錦叠骑、人聲如沸宙枷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睬隶。三九已至页徐,卻和暖如春变勇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戳气。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呀袱,地道東北人夜赵。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓署拟,卻偏偏與公主長得像歌豺,于是被迫代替她去往敵國和親类咧。 傳聞我的和親對象是個殘疾皇子区宇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345