使用python實現歸併排序、快速排序、堆排序
阿新 • • 發佈:2019-12-26
歸併排序
使用分治法:分而治之
分:
- 遞迴地拆分陣列,直到它被分成兩對單個元素陣列為止.
- 然後,將這些單個元素中的每一個與它的對合並,然後將這些對與它們的對等合併,直到整個列表按照排序順序合併為止.
治:
- 將2個排序列表合併為另一個排序列表是很簡單的.
- 簡單地通過比較每個列表的頭,刪除最小的,以加入新排序的列表.
- O(n) 操作
圖示:
動圖:
實現
# 合併 def merge(a, b): c = [] while len(a) > 0 and len(b) > 0: if a[0] < b[0]: c.append(a[0]) a.remove(a[0]) else: c.append(b[0]) b.remove(b[0]) if len(a) == 0: c += b else: c += a return c # 排序 def merge_sort(li): if len(li) <= 1: return li # 整除2 m = len(li) // 2 a = merge_sort(li[:m]) b = merge_sort(li[m:]) return merge(a, b)
演算法分析
- 平均時間複雜度:O(nlog2n)
- 最好時間複雜度:O(nlog2n)
- 最壞時間複雜度:O(nlog2n)
- 空間複雜度:O(n)
- 穩定性:穩定的
快速排序
從數列中挑出一個元素,稱為 “基準”(pivot);
重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。
在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作;
遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序;
實現
簡易版
# 快速排序1 def quick_sort_one(li): if len(li) <= 1: return li v = li[0] left = quick_sort_one([i for i in li[1:] if li[i] <= v]) right = quick_sort_one([i for i in li[1:] if li[i] > v]) return left + v + right
partition分割槽
import random def quick_sort(li, left, right): if left < right: # 待排序的區域至少有兩個元素 mid = partition(li, left, right) quick_sort(li, left, mid - 1) quick_sort(li, mid + 1, right) # partition分割槽 def partition(li, left, right): tmp = li[left] while left < right: while left < right and li[right] >= tmp: right -= 1 li[left] = li[right] while left < right and li[left] <= tmp: left += 1 li[right] = li[left] li[left] = tmp return left li = list(range(100)) random.shuffle(li) quick_sort(li, 0, len(li) - 1) print(li)
演算法分析
- 平均時間複雜度:O(nlog2n)
- 最好時間複雜度:O(nlog2n)
- 最壞時間複雜度:O(n2)
- 空間複雜度:O(nlog2n)
- 穩定性:不穩定的
堆排序
- 建立堆
- 得到堆頂元素,為最大元素
- 去掉堆頂,將堆最後一個元素放到堆頂,此時可通過一次調整重新使對有序
- 堆頂元素為第二大元素
- 重複步驟3,直到堆為空,排序結束
實現
import random def sift(li, low, high): # li表示樹, low表示樹根, high表示樹最後一個節點的位置 tmp = li[low] i = low j = 2 * i + 1 # 初識j指向空位的左孩子 # i指向空位,j指向兩個孩子 while j <= high: # 迴圈退出的第二種情況: j>high,說明空位i是葉子節點 if j + 1 <= high and li[j] < li[j + 1]: # 如果右孩子存在並且比左孩子大,指向右孩子 j += 1 if li[j] > tmp: li[i] = li[j] i = j j = 2 * i + 1 else: # 迴圈退出的第一種情況:j位置的值比tmp小,說明兩個孩子都比tmp小 break li[i] = tmp def heap_sort(li): n = len(li) # 1. 構造堆 for low in range(n // 2 - 1, -1, -1): sift(li, low, n - 1) # 2. 挨個出數 for high in range(n - 1, -1, -1): li[0], li[high] = li[high], li[0] # 退出 棋子 sift(li, 0, high - 1) li = list(range(100)) random.shuffle(li) heap_sort(li) print(li)
演算法分析
- 平均時間複雜度:O(nlog2n)
- 最好時間複雜度:O(nlog2n)
- 最壞時間複雜度:O(nlog2n)
- 空間複雜度:O(1)
- 穩定性:不穩定的
~>.&