插入排序
算法分析
兩次循環, 大循環對隊列中的每一個元素拿出來作為小循環的裁定對象
小循環對堆當前循環對象在有序隊列中尋找插入的位置
性能參數
空間復雜度 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
總結
冒泡排序
重復走訪所有要排序的數據,
依次比較每兩個相鄰的元素,
如果兩者次序錯誤就交換
重復上面過程 直到沒有需要被調換的內容為止
插入排序
將數據插入到已經有序的數據中, 從而得到一個新的有序數據
默認首元素自然有序, 取出下一個元素, 對已經有序的數據從后向前掃描
若掃描的有序數據大於取出數據, 則該有序數據后移
若掃描的有序數據小於取出數據, 則在該有序數據后插入取出數據
若掃描的所有的有序數據大於取出數據, 則在有序數據的首位插入取出數據
特點
數據只移動不交換, 優於冒泡
快速排序
首先任意取一個元素作為關鍵數據 ( 通常取首元素 )
然后將所有比他小的數據源放在其前面
(從小到大)所有比它大的放在他后面
通過一次排序將要排序的數據分為獨立的兩部分
然后按照該方法再遞歸對兩部分數據進行快速排序
特點
每次若能均勻分組則排序速度最快, 但是不穩定