Python - 排序( 插入, 冒泡, 快速, 二分 )


插入排序

算法分析

兩次循環, 大循環對隊列中的每一個元素拿出來作為小循環的裁定對象

小循環對堆當前循環對象在有序隊列中尋找插入的位置

性能參數

空間復雜度  O(1)

時間復雜度  O(n^2)

詳細代碼解讀

import random


def func(l):
    # 外層循環: 對應遍歷所有的無序數據
    for i in range(1, len(l)):
        # 備份 取出數據
        temp = l[i]
        # 記錄取出來的下標值
        pos = i
        # 內層循環: 對應從后往前掃描所有有序數據
        """
        i - 1  >   從最后一個有序數據開始, 即無序數據前一位
        -1  >   掃描到下標 0 為止, 要包括第一個, 因此設置 -1 往后推一位
        -1  >   從后往前掃描
        """
        for j in range(i - 1, -1, -1):
            # 若有序數據 大於 取出數據
            if l[j] > temp:
                # 有序數據后移
                l[j + 1] = l[j]
                # 更新數據的插入位置
                pos = j  # 對應所有有序數據比取出數據大的情況
                # 若有序數據 小於/等於  取出數據
            else:
                pos = j + 1
                break
        # 在指定位置插入數據
        l[pos] = temp


if __name__ == '__main__':
    l = list(range(1, 13))
    random.shuffle(l)
    func(l)
    print(l)

簡單實例

import random


def foo(l):
    for i in range(1, len(l)):
        temp = l[i]
        pos = i
        for j in range(i - 1, -1, -1):
            if temp < l[j]:
                l[j + 1] = l[j]
                pos = j
            else:
                pos = j + 1
                break

        l[pos] = temp
    return l


if __name__ == '__main__':
    l = list(range(13))
    random.shuffle(l)
    print(l)  # [12, 0, 4, 5, 6, 2, 11, 10, 8, 7, 3, 1, 9]
    print(foo(l))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

冒泡排序

算法分析

兩兩比較, 每次比較出一個未排序隊列的最大值,讓只在隊列右側排列

兩次循環, 大循環每次輸出一個當前最大值. 

小循環進行具體的數值比對

性能參數

空間復雜度  O(1)

時間復雜度  O(n^2)

詳細代碼

"""
入學后, 第一次上體育課, 體育老師要求大家排隊, 按照身高從低到高排隊
獲取全班 10 名同學的身高
"""

"""
外層循環
    大循環控制總循環次數                    
內層循環
    小循環控制如歌得出這個最大值
        計算大小, 然后彼此交換
"""

import random

"""
基礎版
"""


def func(l):
    # 外層循環: 走訪數據的次數
    for i in range(len(l) - 1):
        # 內層循環: 每次走訪數據時, 相鄰對比次數
        for j in range(len(l) - i - 1):
            # 要求從低到高
            # 如次序有誤就交換
            if l[j] > l[j + 1]:
                l[j], l[j + 1] = l[j + 1], l[j]

    # 遍歷次數
    print("走訪次數:", i + 1)


"""
升級版
"""


def foo(l):
    # 外層循環: 走訪數據的次數
    for i in range(len(l) - 1):
        # 設置是否交換標志位
        flag = False
        # 內層循環: 每次走訪數據時, 相鄰對比次數
        for j in range(len(l) - i - 1):
            # 要求從低到高
            # 如次序有誤就交換
            if l[j] > l[j + 1]:
                l[j], l[j + 1] = l[j + 1], l[j]
                # 發生了數據交換
                flag = True
        # 如果未發生交換數據, 則說明后續數據均有序
        if flag == False:
            break  # 跳出數據走訪
    # 遍歷次數
    print("走訪次數:", i + 1)


if __name__ == '__main__':
    l = list(range(1, 11))
    random.shuffle(l)
    print("排序前:", l)
    # func(l)
    foo(l)
    print("排序后:", l)

簡單代碼

import random


def foo(l):
    for i in range(len(l) - 1):
        for j in range(len(l) - i - 1):
            if l[j] > l[j + 1] and j != len(l):
                l[j], l[j + 1] = l[j + 1], l[j]

    return l


if __name__ == '__main__':
    l = list(range(13))
    random.shuffle(l)
    print(l)  # [2, 3, 0, 7, 8, 11, 10, 6, 4, 5, 12, 1, 9]
    print(foo(l))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

升級版代碼

import random


def foo(l):
    for i in range(len(l) - 1):
        flag = 1
        for j in range(len(l) - i - 1):
            if l[j] > l[j + 1] and j != len(l):
                l[j], l[j + 1] = l[j + 1], l[j]
                flag = 0
        if flag:
            break
    return l


if __name__ == '__main__':
    l = list(range(13))
    random.shuffle(l)
    print(l)  # [0, 9, 1, 3, 8, 12, 6, 5, 2, 7, 10, 11, 4]
    print(foo(l))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

快速排序

算法分析

首先任意取一個元素作為關鍵數據 ( 通常取首元素

然后將所有比他小的數據源放在其前面, 所有比它大的放在他后面

通過一次排序將要排序的數據分為獨立的兩部分

然后按照該方法再遞歸對兩部分數據進行快速排序

性能參數

時間復雜度  O(nlogn)

空間復雜度  O(logn)

穩定性    不穩定

詳細代碼

# 快速排序
import random


def quick(l):
    # 遞歸退出條件
    # 僅剩一個元素無需繼續分組
    if len(l) < 2:
        return l
        # 設置關鍵數據
    a = l[0]
    # 找出所有比 a 大的數據
    big = [x for x in l if x > a]
    # 找出所有比 a 小的數據
    small = [x for x in l if x < a]
    # 找出所有與 a 相等的數據
    same = [x for x in l if x == a]
    # 拼接數據排序的結果
    return quick(small) + same + quick(big)


if __name__ == '__main__':
    l = list(range(1, 25))
    random.shuffle(l)
    l = quick(l)
    print(l)

二分查找

算法分析

只能對有序隊列進行查找, 利用和中間值進行對比, 然后基於判斷將隊列丟棄一半的方式

性能參數

時間復雜度  O(log2 n)
空間復雜度  O(1)

詳細代碼

"""
1. 切分成兩部分,取中間值來判斷
2. 如何定義下一次的范圍:
    大於中間值, 在左側找
    小於中間值, 在右側找
3. 查找失敗情況: 中間值 小於左端 或者 中間值 大於 右端
"""

"""
撲克牌 只取 黑桃 13 張, 用 1-13 表示, 將牌從小到大排序, 反面向上排成一排, 找到黑桃 6 的位置
"""

"""
l   原始數據
k     待查找數據
left    首元素下標值
right   尾元素下標值
"""

"""
遞歸方式實現
"""


def func(l, k, left, right):
    # 遞歸退出條件
    if left > right:
        # 查找結束
        return -1
    # 獲取中間元素對應下標值
    middle = (left + right) // 2
    # 對比中間元素 和 查找元素
    if l[middle] == k:
        return middle
    elif l[middle] > k:
        # 中間值 大於 查找值
        # 查找范圍是 中分后的 左邊部分
        # 左側下標值不變, 右側下標值變為 middle 前一位
        right = middle - 1
        return func(l, k, left, right)
    else:
        # 中間值 小於 查找值
        # 查找范圍是 中分后的 右邊部分
        # 左側下標值變為 middle 后一位, 右側下標值不變
        left = middle + 1
        return func(l, k, left, right)


"""
循環方式實現
"""



def foo(l, k):
    left = 0
    right = len(l) - 1
    while left <= right:
        mid = (left + right) // 2
        if l[mid] > k:
            right = mid - 1
        elif l[mid] < k:
            left = mid + 1
        elif l[mid] == k:
            return midreturn -1


if __name__ == '__main__':
    # l = list(range(1, 14))
    # k = 8
    # right = len(l) - 1
    # res = func(l, k, 0, right)

    l = list(range(1, 14))
    k = 10
    right = len(l) - 1
    res = foo(l, k)
    if res == -1:
        print("查找失敗")
    else:
        print("查找成功, 第 %d 張拿到" % res)

簡單代碼

def foo(l, k):
    left = 0
    right = len(l) - 1
    while left <= right:
        mid = (left + right) // 2
        if l[mid] > k:
            right = mid - 1
        elif l[mid] < k:
            left = mid + 1
        elif l[mid] == k:
            return mid
    return -1


if __name__ == '__main__':
    l = list(range(13))
    print(l)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    print(foo(l, 8))  # 8

總結

冒泡排序

重復走訪所有要排序的數據,
依次比較每兩個相鄰的元素,
如果兩者次序錯誤就交換
重復上面過程 直到沒有需要被調換的內容為止

插入排序

將數據插入到已經有序的數據中, 從而得到一個新的有序數據

默認首元素自然有序, 取出下一個元素, 對已經有序的數據從后向前掃描

若掃描的有序數據大於取出數據, 則該有序數據后移

若掃描的有序數據小於取出數據, 則在該有序數據后插入取出數據

若掃描的所有的有序數據大於取出數據, 則在有序數據的首位插入取出數據

特點

數據只移動不交換, 優於冒泡

快速排序

首先任意取一個元素作為關鍵數據 ( 通常取首元素 )

然后將所有比他小的數據源放在其前面

(從小到大)所有比它大的放在他后面

通過一次排序將要排序的數據分為獨立的兩部分

然后按照該方法再遞歸對兩部分數據進行快速排序

特點

每次若能均勻分組則排序速度最快, 但是不穩定

 


免責聲明!

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



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