3、排序算法

在這裏插入圖片描述

一、冒泡排序

  • 冒泡排序的思想:

    • 循環把 相鄰的元素進行比較, 當一個元素大於右側相鄰元素時, 交換它們的位置
    • 當一個元素小於或等於右側相鄰元素時, 位置不變
    • 由於該排序算法的每一輪都要遍歷所有元素, 總共遍歷(n-1) 輪, 所以平均時間複雜度是O(n2)
  • 冒泡排序示意圖:
    在這裏插入圖片描述

  • 冒泡排序優化:

    • 利用布爾變量 is_sorted 作爲標記,如果在本輪排序中, 元素有交換, 則說明數列無序; 如果沒有元素交換, 則說明數列已然有序, 然後直接跳出大循環
      在這裏插入圖片描述

    • 在每一輪排序後, 記錄下來最後一次元素交換的位置, 該位置即爲無序數列的邊界, 再往後就是有序區了
      在這裏插入圖片描述

    • 雞尾酒排序:

      • 排序過程就像鐘擺一樣,奇數輪從左到右排序, 偶數輪從右到左排序
      • 針對大部分元素已經有序情況的優化
  • 代碼實踐:

def bubble_sort(alist):
    # 總共需要遍歷 n-1 輪
    for pair_num in range(len(alist) - 1, 0, -1):
        # 每一輪需要 pair_num 對相鄰數據
        is_sorted = True  # 已經有序
        for i in range(pair_num):
            if alist[i] > alist[i + 1]:
                """
                temp = alist[i]
                alist[i] = alist[i + 1]
                alist[i + 1] = temp
                """
                alist[i], alist[i + 1] = alist[i + 1], alist[i]  # python 支持直接交換
                is_sorted = False
        if is_sorted:
            break


alist = [20, 30, 40, 90, 50, 60, 70, 80, 100, 110]  # 加上 is_sorted 只需要二輪即可結束排序
bubble_sort(alist)
print(alist)  # [20, 30, 40, 50, 60, 70, 80, 90, 100, 110]


二、快速排序

  • 快速排序的思想:
    • 在每一輪挑選一個基準元素, 並讓其他比它大的元素移動到右邊, 比它小的元素移動到左邊, 從而把數據表拆解成兩個部分
    • 分治法:原數據表在每一輪都被拆分成兩部分, 每一部分在下一輪又分別被拆分成兩部分, 直到不可再分爲止,排序的時間複雜度爲 O(nlogn),如果只是單純的二分查找,時間複雜度爲 O(logn)
      在這裏插入圖片描述
  • 基準元素(pivot)的選擇:一般會隨機選擇一個,並且讓基準元素和數列首元素交換位置(亦可直接選擇第一個元素)
    在這裏插入圖片描述
  • 元素的交換:雙邊循環法
    • 分裂數據表的目標:找到基準元素應處的位置,比它大的元素移動到右邊, 比它小的元素移動到左邊, 從而把數據表拆解成兩個部分
    • 分裂數據表的手段:設置左右指針(left_idx/right_idx),左指針向右移動,右指針向左移動
      • 左指針一直向右移動,碰到比基準元素大的就停止
      • 右指針一直向左移動,碰到比基準元素小的就停止
      • 然後把左右指針所指的數據項交換
      • 重複上述過程,直到左指針移動到右指針的右側(cross),停止移動,將基準元素和右指針所指元素進行交換
        在這裏插入圖片描述
  • 代碼實踐:
# 基於遞歸的雙邊循環快速排序法
def quick_sort(lst, start_idx, end_idx):
    # 遞歸結束條件:start_idx 大等於 end_idx 的時候
    if start_idx >= end_idx:
        return

    # 得到基準元素應處的位置
    pivot_idx = partition(lst, start_idx, end_idx)

    # 根據基準元素,分成兩部分遞歸排序
    quick_sort(lst, start_idx, pivot_idx - 1)  # left quick sort
    quick_sort(lst, pivot_idx + 1, end_idx)  # right quick sort


def partition(lst, start_idx, end_idx):
    """找到基準元素應處的位置,比它大的元素移動到右邊,
    比它小的元素移動到左邊"""

    # 取第一個位置的元素作爲基準元素(也可以選擇隨機位置)
    pivot_value = lst[start_idx]

    # 左右指針初始索引值
    left_idx = start_idx + 1
    right_idx = end_idx

    split_point_founded = False
    while not split_point_founded:
        # 向右移動左指針
        while left_idx <= right_idx and lst[left_idx] <= pivot_value:
            left_idx += 1

        # 向左移動右指針
        while left_idx <= right_idx and lst[right_idx] >= pivot_value:
            right_idx -= 1

        # 兩指針相互交錯則結束移動,否則交換左右指針所指向的元素
        if right_idx < left_idx:
            split_point_founded = True
        else:
            temp = lst[left_idx]
            lst[left_idx] = lst[right_idx]
            lst[right_idx] = temp

    # 將基準元素和右指針所指元素進行交換
    temp = lst[start_idx]
    lst[start_idx] = lst[right_idx]
    lst[right_idx] = temp

    return right_idx  # 返回基準元素點,也就是分裂點


alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
quick_sort(alist, 0, len(alist) - 1)
print(alist)  # [17, 20, 26, 31, 44, 54, 55, 77, 93]

三、堆排序

  • 把無序數組構建成二叉堆。 需要從小到大排序, 則構建成最大堆; 需要從大到小排序, 則構建成最小堆。
  • 循環刪除堆頂元素, 替換到二叉堆的末尾, 調整堆產生新的堆頂

四、計數排序

  • 計數排序的思想:
    • 直接遍歷數組, 輸出數組元素的下標值, 元素的值是幾, 就輸出幾次
    • 它適用於一定範圍內的整數排序
  • 數組長度優化方法:
    • 以數列(最大值 - 最小值 + 1)作爲統計數組的長度
    • 數列的最小值作爲一個偏移量, 用於計算整數在統計數組中的下標

五、桶排序

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章