1. 程式人生 > >使用python實現歸併排序、快速排序、堆排序

使用python實現歸併排序、快速排序、堆排序

歸併排序

使用分治法:分而治之

分:

  • 遞迴地拆分陣列,直到它被分成兩對單個元素陣列為止.
  • 然後,將這些單個元素中的每一個與它的對合並,然後將這些對與它們的對等合併,直到整個列表按照排序順序合併為止.

治:

  • 將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)
  • 穩定性:不穩定的

堆排序

  1. 建立堆
  2. 得到堆頂元素,為最大元素
  3. 去掉堆頂,將堆最後一個元素放到堆頂,此時可通過一次調整重新使對有序
  4. 堆頂元素為第二大元素
  5. 重複步驟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)
  • 穩定性:不穩定的

~>.&