1. 程式人生 > >【5】資料結構與演算法--- 演算法 進階

【5】資料結構與演算法--- 演算法 進階

第 4 章 演算法 進階

4.1 排序

4.1.1 排序演算法簡介

排序:把無序的佇列變成有序的佇列

排序演算法:排序演算法是一種將一串無規律資料依照特定順序進行排列的一種方法或思路

排序演算法的穩定性:佇列中有相同的元素,排序前後,這兩個相同元素的順序有沒有發生變化。

特點:

輸入:無序佇列 輸出:有序佇列

應用場景:

各種排行榜 - 服不服排行榜

各種表格 - 座位表

排序演算法的關鍵點:

有序佇列:有序區剛開始沒有任何資料,逐漸變多

無序佇列:有序區剛開始存放素所有資料,逐漸變空

排序演算法的穩定性

佇列中相同元素排序前後

  • ​ 沒有發生變化,這表示演算法有穩定性 ​ 發生變化,表示沒有穩定性

常見的排序演算法:

三基冒插選,中快高堆歸,其基希爾桶,冒快會歸併

常見排序演算法 演算法
基礎 冒泡、插入、選擇
中級 快速
高階 堆、歸併
其他 基數、希爾、桶

4.1.2 氣泡排序

氣泡排序:

​ 相鄰的元素兩兩比較,升序的話:大的在右,小的在左,降序的話,反之。經過數次比較迴圈,最終達到一個從小到大(升序)或者從大到小(降序)的有序序列.這個演算法由於類似於氣泡從水底冒出,所以叫“冒泡”排序。

過程跟蹤

​ 在整個氣泡排序過程中,有一個標識指向兩個元素的最大值,當這個最大值移動的時候,標識也會隨之移動,這就叫做:過程跟蹤

特點:

  • - 元素替換:相鄰元素從小到大:
    
      		左比右大,資料先交換位置,大的和右側的元素繼續比較
    
      		左比右小,資料不交換位置,大的和右側的元素繼續比較
    
      		左右相等,資料不交換位置,大的和右側的元素繼續比較
    
    - 比較次數:無序佇列元素個數 - 1 
    
    - 冒泡次數:無序佇列元素個數 - 1 
    
    - 冒泡次數和比較次數關係:
    
      - 冒泡次數:1 ===> n
      - 比較次數:n ===> 1
    
    - 過程跟蹤:alist[i]=最大值
    

氣泡排序的分析:

>- 元素替換:最基本元素比較(列表的下表)
>
>- 內層比較迴圈:每次氣泡排序,內層的元素比較次數
>
>  ​	比較次數 + 元素範圍(下標)
>
>- 外層冒泡迴圈:執行多少次氣泡排序
>
>  ​	冒泡次數、冒泡次數和元素比較次數關係
>
>- 不替換情況:特殊情況
>
>  ​	計數器

氣泡排序的實踐:

def bubble_sort(alist):
    """氣泡排序"""
    # 獲取列表元素的總數量
    n = len(alist)

    # 3.氣泡排序迴圈範圍
# 氣泡排序只關注排序次數 for j in range(n - 1, 0, -1): # 開始比較前,定義計數器 count 的初始值為 0 count = 0 # 2.內層的資料比較迴圈範圍 for i in range(j): # 1.相鄰元素替換 if alist[i] > alist[i + 1]: alist[i], alist[i + 1] = alist[i + 1], alist[i] # 資料替換完畢,計數器加 1 count += 1 # 4.不替換情況 # 如果計數器的值為 0,表示沒有發生任何替換,那麼就退出當前迴圈 if count == 0: break if __name__ == '__main__': li = [54, 26, 93, 17, 77, 31, 44, 55, 20] print(li) bubble_sort(li) print(li)

時間複雜度

最優時間複雜度:O(n)

最壞時間複雜度:O(n^2)

穩定性:穩定

拓展:降序<

4.1.3 選擇排序

簡單直觀

從無序佇列裡面挑選最小的元素,和無序佇列頭部元素替換(放到有序佇列中),最終全部元素形成一個有序的佇列。

選擇排序的原理:

選擇排序的主要特點與元素替換有關。

每次移動一個元素,就有一個元素放到有序佇列,n個元素的無序佇列最多進行(n-1)次交換,就可以形成有序佇列。

如果整個佇列已排序,那麼它不會被移動。

選擇排序的分析:

  • 比較迴圈:無序佇列查詢最小元素

    ​ mix表示同意最小元素

    ​ cur標識用於遍歷所有元素

    ⚠️注意:過程跟蹤:mix永遠指向最小的,cur指向的元素負責對比

    mix 和 cur 標籤的初始化地址是相鄰的:

    mix標籤所在元素的下標是j,那麼cur標籤所在元素的下標是 j+1

  • 元素替換:最小元素和無序佇列第一個元素替換位置

  • **選擇迴圈:**需要多少次替換,才能形成有序佇列

選擇排序的實踐

def selection_sort(alist):
    """選擇排序"""
    # 獲取列表元素的總數量
    n = len(alist)

    # 3. 選擇迴圈
    # 定義一個外圍迴圈次數
    # 排序次數範圍的確定
    for j in range(n - 1):
        
        # 定義min_index 初始值
        min_index = j

        # 1.比較迴圈
        # cur 標籤元素下標移動範圍(1,n-1)
        for i in range(j + 1, n):
            # 找到最小的元素
            if alist[i] < alist[min_index]:
                min_index = i

        # 2.元素替換
        # 保證最新的 min_index 不在無序佇列首位,那麼就將它和無序佇列的首個元素進行替換
        if min_index != j:
            # mix 標籤元素和無序佇列首位置元素替換
            alist[j], alist[min_index] = alist[min_index], alist[j]


if __name__ == '__main__':
    li = [11, 3, 6, 33, 5, 8, 2, 88]
    print(li)
    selection_sort(li)
    print(li)
關鍵點:
1、mix標籤初始化:min_index = j 
2、比較迴圈的範圍:for i in range(j+1,n) 
3、元素替換的條件:if min_index != j 
4、排序次數範圍的確定:for j in range(n-1)

時間複雜度

最優時間複雜度:O(n^2)

最壞時間複雜度:O(n^2)

穩定性:看情況

4.1.4 插入排序

先定義一個有序佇列,然後把無序佇列中的第一個元素放到有序佇列的 合適位置,重複操作,直至形成一個完整的有序佇列

插入排序原理

1、構建有序序列

2、選擇無序佇列的第一個元素,先放在有序佇列末尾,然後進行氣泡排序,放到指定的位置

3、迴圈2步,直到無序佇列中所有元素全部進入有序佇列的合適位置

特點:

(1)插入 ?冒泡

  • 聯絡:插入排序的有序佇列用到了冒泡演算法

  • 區別:升序情況下,

    ​ 冒泡:有序佇列在後,無序佇列在前(無序佇列 + 有序佇列);

    ​ 插入:有序佇列在前,無序佇列在後(有序佇列 + 無序佇列)

(2)插入?選擇

  • 選擇:遍歷未排序佇列,將最小的元素移動到有序佇列的末尾;
  • 插入:把無序佇列的第一個元素放到有序佇列,通過使用冒泡演算法,移動到合適的位置。

插入排序分析

元素替換:有序佇列中元素比較替換

比較迴圈:每次排序,有序佇列元素比較替換的次數

排序迴圈:需要進行多少次排序

插入排序實踐

def insert_sort(alist):
    """插入排序"""
    # 無序佇列元素數量
    n = len(alist)

    # 3.插入排序迴圈
    # 有序佇列迴圈的次數
    for i in range(n):

        # 2.比較迴圈次數的確定
        # 有序佇列末尾元素下標為i,範圍(0,i]
        for j in range(i, 0, -1):

            # 1.元素替換
            # 有序列表的兩個元素進行比較
            if alist[j] < alist[j - 1]:
                # 大小值元素替換
                alist[j], alist[j - 1] = alist[j - 1], alist[j]
            # 條件不滿足,大小元素不替換
            else:
                break


if __name__ == '__main__':
    li = [11, 3, 6, 33, 5, 8, 2, 88]
    print(li)
    insert_sort(li)
    print(li)
關鍵點:
1、元素替換:if alist[j] < alist[j-1] 
2、比較迴圈:for j in range(i,0,-1) 
3、插入迴圈:for i in range(n):

時間複雜度

最優時間複雜度:O(n)

最壞時間複雜度:O(n^2)

穩定性:穩定

4.1.5 希爾排序

希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是插入排序演算法的一種高效的改進版本

希爾排序原理:

兩兩一組、四四一組、八八一組…,直到所有元素為一組,進行排序

特點:

下標增量分組,對小組元素進行插入排序

下標增量的特點:
第一次分組,gap=n/2 ,
從第二次分組,gap=gap/2,
最後一次分組gap=1
整個分組過程就是:遞迴

希爾排序分析:

  • 元素替換:分組佇列中元素比較替換

    ​ 下標的範圍必須大於0

  • 比較次數:每次分組後,同時有幾組在進行比較

    ​ 插入排序

  • 分組次數:需要進行多少次分組

希爾排序實踐:

def shell_sort(alist):
    """希爾排序"""
    # 獲取列表的長度
    n = len(alist)

    # 3.遞迴分組迴圈
    # 獲取下標偏移量gap(取整)
    gap = n // 2
    # 只要gap在合理範圍內,就一直分組下去
    while gap >= 1:
        
        # 2.比較迴圈(多少組進行插入排序)
        # 指定i下標的取值範圍
        for i in range(gap, n):

            # 1.元素替換
            # 對移動元素的下標進行條件判斷
            # 下標的範圍必須大於0
            while (i - gap) >= 0:
                # 組內大小元素進行替換
                if alist[i] < alist[i - gap]:
                    alist[i], alist[i - gap] = alist[i - gap], alist[i]
                    # 修改i 的屬性重新指向原始元素(過程跟蹤)
                    i = i - gap
                # 否則的話,不進行替換
                else:
                    break
        # 沒執行完一次分組內的插入排序,對gap進行/2細分
        gap = gap // 2


if __name__ == '__main__':
    li = [11, 3, 6, 33, 5, 8, 2, 88]
    print(li)
    shell_sort(li)
    print(li)
1、元素替換:
    下標範圍:while (i - gap) >= 0: 
    替換條件:if alist[i] < alist[i-gap]: 
    過程跟蹤:i = i - gap
2、比較迴圈:
	元素的範圍:for i in range(gap,n):
3、遞迴分組迴圈
	偏移量初始值:gap = n // 2 
	遞迴迴圈的退出條件:while gap >= 1 
	gap偏移量規律:gap = gap // 2

時間複雜度

最優時間複雜度:O(nlogn)~O(n^2)

最壞時間複雜度:O(n^2)

穩定性:不穩定

4.1.6 快速排序

快速排序,又稱劃分交換排序,從無序佇列中挑取一個元素,把無序佇列分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。

挑元素、劃分組、分組重複前兩步

快速排序原理

挑元素劃分組,整體遞迴分組

特點:

1、因為是無序佇列,所以位置可以隨機挑

2、臨時劃分一個空間,存放我們挑選出來的中間元素

3、左標籤位置空,移動右標籤,反之一樣

4、重複3,直到左右側標籤指向同一個位置,

5、把臨時存放的中間元素,歸位

左手右手一個慢動作,右手左手慢動作重播

整體劃分特點:

1、遞迴拆分

2、拆分到最後,所有小組內的元素個數都是1

遞迴拆分到不能再拆

快速排序分析

  • 序列切割

    ​ 三個基本標籤:

    ​ mid:指定要切割的臨時中間數字

    	left:從佇列左側推進的標籤 
    

    ​ right:從佇列右側推進的標籤

    ​ left永遠小於right

    ​ 右側推進

    ​ 左側推進

    ​ 停止推進 (即元素歸位)

  • 遞迴切分

    ​ 遞迴拆分:小組邊界的確定 和 遞迴功能實現

    ​ 左側邊界start:0 右側邊界end:left-1

    ​ 左側邊界start:left+1 右側邊界end:len(alist)-1

    ​ 遞迴退出條件

快速排序實踐

# 2.遞迴切分
# 2.1 小組邊界確定
# 增加兩個引數,左邊界 start,右邊界 end
def quick_sort(alist, start, end):
    """快速排序"""
    # 2.3定義遞迴退出條件
    if start < end:

        # 1.序列切割
        #  1.1定義三個基本標籤
        #  因為 mid 指定的是傳入列表的左邊界元素
        mid = alist[start]
        left = start
        right = end

        # 定義拆分條件
        while left < right:
            # 1.2 右側推進
            # 如果right元素 > mid值,right標籤左移
            while right > left and alist[right] >= mid:
                right -= 1
            # 如果right元素 < mid值,left標籤元素設定為right標籤元素
            alist[left] = alist[right]

            # 1.3 左側推進
            # 如果left元素 < mid值,left標籤右移
            while left < right and alist[left] < mid:
                left += 1
            # 如果left元素 > mid值,right標籤元素設定為left標籤元素
            alist[right] = alist[left]

        # 1.4 停止標籤(元素歸位)
        # 退出迴圈,表示 left和right 標籤合併在一起了
        # 獲取中間值
        alist[left] = mid

        # 2.2 遞迴功能的實現
        # 函式自呼叫
        # 對切割後左邊的子部分進行快速排序
        quick_sort(alist, start, left - 1)
        #  對切割後右邊的子部分進行快速排序
        quick_sort(alist, left + 1, end)


if __name__ == "__main__":
    li = [54, 26, 93, 17, 77, 31, 44, 77, 20]
    print(li)
    quick_sort(li, 0, len(li) - 1)
    print(li)
序列切割:
    1、挑中間元素:mid = alist[start]
    2、右推進:while right > left and alist[right] >= mid: 
    3、左推進:while left < right and alist[left] < mid: 
    4、推進迴圈:while left < right: 
    5、元素歸位:alist[left] = mid
遞迴拆分:
    1、小組邊界確定:left = start、right = end 
    2、遞迴退出條件:if start < end: 
    3、函式自呼叫:quick_sort(alist, start, end)

時間複雜度

最優時間複雜度:O(nlogn)

最壞時間複雜度:O(n^2)

穩定性:不穩定

4.1.7 歸併排序

歸併排序是採用分治法的一個非常典型的應用。

將無序佇列拆成兩個小組,組內元素排序,然後組間元素逐個比較,把小元素依次放到新佇列中。

關鍵字:拆分、排序、組間、小、新佇列

分組排序,合併新佇列

歸併排序原理

  • 分組排序階段:

    ​ 1、將無序佇列alist,拆分成成兩個小組A和B,

    ​ 2、分別對兩個小組進行同樣的氣泡排序

    ​ 3、用標籤left和right,分別對小組A和B進行管理

  • 合併新佇列階段:

    ​ 4、兩個標籤所在的元素比較大小,

    ​ 5、將小的元素放到一個新佇列中,然後小元素所在的標籤向右移

    6、多次執行4和5,最終肯定有一個小組先為空
    
    7、把不為空的小組元素,按順序全部移到新佇列的末尾 
    

    ​ 8、無序佇列中的所有元素就在新佇列中形成有序隊列了

特點:

​ 兩個階段:分組排序 + 合併

​ 合併策略:組間比較、新增小,小移標

兩種情況:

​ 分兩組合並排序

​ 遞迴分組合並排序:層級分組、排序、層級合併

歸併排序分析

  • 分組實現

    ​ 首次分組

    ​ 正常分組

    ​ 不能分組:空佇列/佇列只有一個元素

    ​ 遞迴分組

    ​ 合併分組

  • 合併分組的排序

    準備工作

    ​ 組標籤:定義兩個標籤l和r,分別是分組列表的首 位下標值0

    ​ 組長度

    ​ 空佇列

    空列表增加資料

    ​ 空列表新增資料

    ​ 一組空,另一組剩餘元素按順序一次性新增到空列

歸併排序實踐

def fen_zu(alist):
    """分組"""
    # 獲取當前序列的長度
    n = len(alist)
    # 1.首次分組
    #   1.2 不能分組
    # 佇列異常情況
    if n <= 1:
        return alist
    #   1.1 正常分組
    # 把當前列表分成兩部分,使用切片方式h獲取兩部分內容
    mid = n // 2
    # 左半部分
    # left = alist[:mid]
    # 右半部分
    # right = alist[mid:]

    # 2.遞迴分組
    # 左半邊資料
    zuo = fen_zu(alist[:mid])
    # 右半邊資料
    you = fen_zu(alist[mid:])

    # 3.合併分組
    # 將分組後的資料交個一個合併資料的函式去處理
    return merge(zuo, you)


def merge(zuo, you):
    """歸併排序"""

    # 3.1 準備工作
    # (1)組標籤
    # 定義標籤 l 和 r 在兩組的位置
    l, r = 0, 0
    # (3)空佇列
    # 定義一個空列表
    result = []
    # (2)組長度
    # 獲取兩個分組的長度
    zuo_len = len(zuo)
    you_len = len(you)

    # 指定標籤的有效範圍
    while l < zuo_len and r < you_len:
        # 3.2 空列表新增資料
        # 判斷兩側標籤指定的資料大小
        if zuo[l] <= you[r]:
            # 將左側小資料追加到新佇列
            result.append(zuo[l])
            # left標籤右移一位
            l += 1
        else:
            # 將右側小資料追加到新佇列
            result.append(you[r])
            # right標籤右移一位
            r += 1

    # 3.3 一組為空,另一組剩餘元素按順序新增到新佇列
    # 將左側的剩餘內容,一次性新增到 result 表中
    result += zuo[l:]
    # 將右側的剩餘內容,一次性新增到 result 表中
    result += you[r:]

    # 返回 result 表
    return result


if __name__ == "__main__":
    li = [54, 26, 93, 17, 77, 31, 44, 77, 20]
    print("處理前: %s" % li)
    sort_list = fen_zu(li)
    print("處理後: %s" % li)
    print("新列表: %s" % sort_list)
關鍵點:
1、異常分組:if n <= 1: 
2、遞迴分組:fen_zu(alist[:mid]) 
3、分組合並:merge(zuo, you)
	1、資料比較條件:while l < zuo_len and r < you_len: 
	2、小元素移動:result.append(zuo[l]) 
	3、小元素標籤處理:l += 1
	4、異常情況:result += zuo[l:]
	5、最終效果:return result

時間複雜度

最優時間複雜度:O(nlogn)

最壞時間複雜度:O(nlogn)

穩定性:穩定

4.1.8 堆排序

堆是採用順序表儲存的一種近似完全二叉樹的結構。

父節點和子結點關係(父找子):

左子節點位置:2i + 1 右子節點位置:2i + 2

堆分類

堆分類
大頂堆 任一節點都比其孩子節點大最大值 堆頂元素 alist[0]
小頂堆 任一節點都比其孩子節點小

它是指利用堆這種樹結構所設計的一種排序演算法。

將無序列表先構造一個有特點的堆,然後利用列表的特點快速定位最大/小的元素,將其放到一個佇列中。

特點:

無序佇列構建一個堆,堆頂和堆尾元素替換位置

重新構建堆,堆頂和堆尾元素替換位置,…

頭尾替換,恢復堆後再繼續

堆排序原理

構建一個堆:從最後一個有子節點的節點開始構建,下標為[n/2-1]

堆的調整:移除堆頂元素,用佇列中最後一個元素填補,自上而下進行調整

​ 1 將無序列表構造為一個標準的大頂堆 2 將堆頂元素和堆尾元素進行替換 相當於將最大元素放到了有序序列 剩餘的無序序列少了一個 3 將剩餘的無序序列重新調整為標準的大頂堆 4 重複2-3 最終形成一個有序序列

堆排序分析

堆的調整:

​ 準備工作

​ 佇列引數:data

​ 堆頂元素的確定:傳入一個堆頂佇列的下標low

​ 堆頂元素的臨時存放空間:tmp

​ 無序佇列中元素的最大下標值:high

​ 選擇新的堆頂節點

​ 選大的子節點

​ 最大子節點跟移除的堆頂元素進行比較

堆頂元素排序

​ 堆的構造

堆頂元素輸出到有序佇列

		步驟 
				1 無序序列構造標準大頂堆
				2 堆頂和堆尾元素替換
				3 剩餘無序序列調整大頂堆
				4 重複2-3 

堆排序實踐

關鍵點
				1 無序序列構造標準大頂堆
					從最後一個包含子節點的父節點開始構造
						n/2 -1
						順序:從下向上來構造的
						n/2 -1  n/2 -2   n/2 -3 。。。0 
						表示式:
							range(int(n/2)-1,-1,-1)
				2 堆頂和堆尾元素替換
					無序列表是alist
					堆頂位置  0
					假設堆尾下標是z 
					替換:	
						alist[0],alist[z] = alist[z],alist[0]
					
				3 剩餘無序序列調整大頂堆
					3.1 把臨時堆頂元素放到臨時空間tmp
						tmp = alist[0]
					3.2 選擇一個最大的子節點
					3.3 最大子節點和臨時堆頂元素比較
						如果臨時堆頂元素大,那麼歸位
						如果子節點大,那麼最大子節點放到堆頂位置
							假設子節點下標為j  alist[0] = alist[j]
						3.4 沿著破壞的路徑繼續調整下去
							i = j
							j = 2*i + 1
					原則:
						從上到下
					調整的時候,:	
						物件 alist 
						堆頂位置 low
						調整的範圍 	high
							
				4 重複2-3
					在替換的過程中,堆尾元素的下標z變化是:
						n-1 n-2 n-3 n-4 ..... 0 
						表示式:
							range(n-1,-1,-1)
def sift(alist, low, high):
    """堆的調整"""
    # 1.準備工作:堆頂元素的標籤 + 臨時佇列
    # 指定移除的堆頂位置元素下標為 i
    i = low
    # 將移除的堆頂元素存放到一個臨時佇列tmp
    tmp = alist[i]

    # 2.選擇新的堆頂節點
    # 假設左子節點是大節點,下標是 j
    j = 2 * i + 1
    # j 下標的操作範圍
    # 無序佇列中元素的最大下標值
    # 左側子節點小於堆的最大範圍值
    while j <= high:
        # 2.1 選取最大子節點
        # 左右子節點進行比較
        if j + 1 <= high and alist[j] < alist[j + 1]:
            # 過程跟蹤,保證通過 j 找到最大元素
            # 上移節點標號 j 指向右側節點
            j += 1

        # 2.2 最大子節點 和 原堆頂節點比較
        # 子結點 > 原堆頂節點
        if alist[j] > tmp:
            # 將子節點元素移動到堆頂元素
            alist[i] = alist[j]
            # 因為子節點位置空了,相當於堆頂節點移除了,又要重複操作,所以需要更新 i 和 j 的值
            i = j
            j = 2 * i + 1

        # 如果最大的子節點小於移除的堆頂元素,終止該操作即可
        else:
            break

    # 臨時堆頂元素歸位
    # 設定堆頂節點為原來的內容
    alist[i] = tmp


def heap_sort(alist):
    """堆排序"""
    # 獲取當前列表長度
    n = len(alist)

    # 1.無序列表構造標準大堆頂
    # 對所有父節點進行堆的調整,而且是降序排列(從下往上構造)
    # 最後一個元素是n,其父元素尾n/2-1
    for i in range(int(n / 2) - 1, -1, -1):
        sift(alist, i, n - 1)

    # 堆頂元素排序
    # 指定最小元素的範圍
    for z in range(n - 1, -1, -1):
        # 2.堆頂元素與堆尾元素替換
        # 佇列中最大元素和最小元素進行替換
        alist[0], alist[z] = alist[z], alist[0]
        # 3.剩餘無序列表調整大頂堆(從上到下)
        # 調整新佇列
        # 替換完畢後,重新調整堆結構,新的堆結構元素個數變成了 i-1 個
        sift(alist, 0, z - 1)
    # 返回最終的有序佇列
    return alist


if __name__ == '__main__':
    a = [0, 2, 6, 98, 34, 5, 23, 11, 89, 100, 7]
    print("排序之前:%s" % a)
    c = heap_sort(a)
    print("排序之後:%s" % c)

時間複雜度

成本:

​ 最優: O(nlogn)

​ 最壞: O(nlogn)

穩定性:不穩定

4.1.9 排序總結

		技術:

				冒小左移,選追加

				插入合適,分希爾

				快速兩半,歸新列

				順表構造首尾堆
		
		成本:
			冒泡  插入 選擇 希爾 堆 歸併 快速  系統

技術總結

氣泡排序 在無序佇列中選擇最小的移動到最左側
選擇排序 定一個有序佇列,從無序佇列中選擇最小的元素追加到有序佇列的末尾
插入排序 定一個有序佇列,從無序佇列中選擇第一個元素,插入到到有序佇列的合適位置
希爾排序 通過對無序佇列進行分組,然後再採用插入的排序方法
快速排序 指定一個元素,將無序佇列拆分為大小兩部分,然後層級遞進,最終實現有序佇列
歸併排序 是將無序佇列拆分,然後小組內排序,組間元素比較後在新佇列中進行排序
堆 排 序 順序表方式構造堆,首尾替換調整堆
冒小左移 選追加,插入合適 分希爾,快速兩半 歸新列,順表構造首尾堆

成本總結

排序方法 時間複雜度 穩定性 程式碼複雜度
最壞情