瞭解和實現冒泡排序、選擇排序、插入排序、希爾排序、歸併排序、和快速排序。python
1.冒泡排序
冒泡排序要對一個列表屢次重複遍歷。算法
它們的順序是否正確。shell
def bubble_sort(alist): n = len(alist) # 外層循環控制比較幾回 for i in range(n-1): # 內存循環控制 第i次 交換 n-i 趟 # -i 是再也不換前i次已經排好的 for j in range(n-i-1): if alist[j] > alist[j+1]: alist[j], alist[j+1] = alist[j+1], alist[j] # print(alist)
因爲冒泡排序要遍歷整個未排好的部分,它能夠作一些大多數排序方法作不到的事。app
def shortBubbleSort(alist): n = len(alist) # 外層循環控制比較幾回 for i in range(n-1): # 假設已經徹底排好序 sorted = True # 內存循環控制 第i次 交換 n-i 趟 # -i 是再也不換前i次已經排好的 for j in range(n-1-i): if alist[j] > alist[j+1]: alist[j], alist[j+1] = alist[j+1], alist[j] # 未徹底排好序 sorted = False if sorted: # 發現列表已排好時馬上結束 return print(alist)
2.選擇排序函數
選擇排序提升了冒泡排序的性能,post
def selectionSort(alist): # 外層控制比較幾回,一共須要 n-1 次遍從來排好 n 個數據 for fillslot in range(len(alist)-1, 0, -1): # 假設第一次元素就是最大值 position_max = 0 # 內層控制元素比較和更新索引 for location in range(1, fillslot+1): # 進行比較 if alist[location] > alist[position_max]: # 更新索引 position_max = location # 退出循環後,交換數據 alist[fillslot], alist[position_max] = alist[position_max], alist[fillslot]
測試:性能
if __name__ == "__main__": alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] print(alist) selectionSort(alist) print(alist)
3.插入排序
插入排序的算法複雜度仍然是O(n2),但其工做原理稍有不一樣。測試
圖4展現了插入排序的過程。陰影區域的數據表明了程序每運行一步後排好的子表。ui
def insertionSort(alist): # 外層循環控制 從右邊第二個元素開始 向前面排好序的子列表中插入 for index in range(1, len(alist)): pos = index # 內存循環 依次從子列表的最後一個最大的元素 和 你要插入的元素比較 # 若是你的當前要插入的元素小,兩個元素交換位置 while pos > 0 and alist[pos-1] > alist[pos]: alist[pos], alist[pos - 1] = alist[pos-1], alist[pos] pos -= 1
測試:spa
if __name__ == '__main__': lst = [54, 26, 93, 17, 77, 31, 44, 55, 20] print(lst) insertionSort(lst) print(lst)
4. 希爾排序
希爾排序有時又叫作「縮小間隔排序」,它以插入排序爲基礎,將原來要排序的列表劃分爲一些子列表,再對每個子列表執行插入排序,從而實現對插入排序性能的改進。劃分子列的特定方法 是希爾排序的關鍵。咱們並不是將原始列表分紅含有連續元素的子列,而是肯定一個劃分列表的增量「i」,這個i更準確地說,是劃分的間隔。而後把每間隔爲i 的全部元素選出來組成子列表。
這裏有一個含九個元素的列表。
在這個例子中,咱們僅須要再進行四次移動就能夠完成排序。
def shellSort(alist): # 咱們從含2個元素的子列開始排序, 子列表的數目 就是列表長度的一半 sublistcount = len(alist)//2 while sublistcount > 0: # 每一個子列表執行插入排序 for startpostion in range(sublistcount): # 間隔正好是 子列表的數量 print("alist: %s" % alist, "startpostion: %d" % startpostion, "sublistcount %d" % sublistcount) gapInsertionSort(alist, startpostion, sublistcount) print("After increments of size", sublistcount, "The list is", alist) sublistcount = sublistcount // 2 def gapInsertionSort(alist, start, gap): for i in range(start+gap, len(alist), gap): pos = i while pos >= gap and alist[pos-gap] > alist[pos]: alist[pos], alist[pos-gap] = alist[pos-gap], alist[pos] pos -= gap
測試:
if __name__ == '__main__': # 奇數次第一組第一次是三個元素 [54,77,20] # alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] # 偶數次比較好看 alist = [54, 26, 93, 17, 77, 31, 44, 55] shellSort(alist) print(alist)
縮減版:
def shell_sort(lst): step = int(len(lst)/2) while step > 0: for i in range(step, len(lst)): while i >= step and lst[i] < lst[i - step]: lst[i], lst[i - step] = lst[i - step], lst[i] i -= step step = int(step/2) print(lst) alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] shell_sort(alist)
對希爾排序的綜合算法分析已經遠超出討論範圍,基於對該算法的描述,
5.歸併排序
咱們如今把注意力轉移到用分而治之的策略來改進排序算法的表現。
def merge_sort(alist): n = len(alist) # 遞歸結束條件 if n <= 1: return alist # 中間位置 mid = n // 2 # 遞歸拆分左側 left_lst = merge_sort(alist[:mid]) # 遞歸拆分右側 right_lst = merge_sort(alist[mid:]) return merge(left_lst, right_lst) def merge(left, right): print(left, right) # 須要兩個遊標,分別指向左列表和右列表的第一個元素 left_point, right_point = 0, 0 # 定義最終返回的結果集 result = [] # 循環合併數據 while left_point < len(left) and right_point < len(right): # 誰小放前面 if left[left_point] <= right[right_point]: # 放入結果集 result.append(left[left_point]) # 遊標移動 left_point += 1 else: result.append(right[right_point]) right_point += 1 # print("合併數據後:", result) # print('left: ', left[left_point:]) # print('right: ', right[right_point:]) # 退出循環時,造成左右兩個序列 result += left[left_point:] result += right[right_point:] return result if __name__ == '__main__': li = [54, 26, 93, 17, 77, 31, 44, 55, 20] lst = [8, 3, 2, 6, 7, 1, 5, 4] print(lst) sort_lst = merge_sort(lst) print(lst) print(sort_lst)
爲了分析歸併算法,咱們須要考慮它實施的兩個不一樣步驟。
6.快速排序
快速排序用了和歸併排序同樣分而治之的方法來得到一樣的優點,但同時不須要使用額外的存儲空間。
通過權衡以後,咱們發現列表不分離成兩半是可能的,當這發生的時候,咱們能夠看到,操做減小了。
快速排序算法的工做原理以下:
# first 理解爲第一個位置的索引,last 是最後位置索引 def quick_sort(alist, first, last): # 遞歸停止條件 if first >= last: return # 設置第一個元素爲中間值 mid_value = alist[first] # low 指向 low = first # high high = last # 只要 low 小於 high 就一直走 flag = 0 while low < high: # high 大於中間值,則進入循環 while low < high and alist[high] >= mid_value: # high 往左走 high -= 1 # 出循環後,說明high小於中間值,low指向該值 alist[low] = alist[high] # low 小於中間值,則進入循環 while low < high and alist[low] < mid_value: # low 往右走 low += 1 # 出循環後,說明low 大於中間值,high 指向該值 alist[high] = alist[low] if not flag: print(alist) # 退出整個循環後,low 和 high 相等 # 將中間值放到中間位置 alist[low] = mid_value print(alist) flag = 1 # 遞歸 # 先對左側快排 quick_sort(alist, first, low-1) # 對右側快排 quick_sort(alist, low+1, last)
測試:
if __name__ == "__main__": lst = [54, 26, 93, 17, 77, 31, 44, 55, 20] print("原來的list:\n", lst) quick_sort(lst, 0, len(lst) - 1) print("快速排序後的list: \n", lst)
各類排序方法的比較: