1. 程式人生 > >排序演算法: 三大中級排序演算法,原理解析及用法

排序演算法: 三大中級排序演算法,原理解析及用法

三大中級演算法

  • 難度 ★★
  • 演算法複雜度O(nlogn)
  • 一般情況下排序時間: 快速排序< 歸併排序 < 堆排序
  • 快速排序: 缺點極端情況下效率低
  • 堆排序: 缺點在快的排序演算法中相對慢
  • 歸併排序: 缺點要有額外記憶體空間

快速排序 ★★

quick Sort

演算法複雜度: O(nlogn) <n乘logn>

思路

每趟取第一個元素,讓列表被該元素分為兩部分,左邊的比他小,右邊比他大, 重複用每次得來的第一個元素遞迴

問題1:

遞迴py有最大深度問題999次!(雖然可以設定)

問題2:

最壞的情況,如果列表本身是一個倒序列表,那麼效率相對較低(解決方案:隨機快排,一開始就隨機取一個數放在最左邊開始排)

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)

def partition
(li, left, right): """ 將下標為0的元素為參照物,左邊的放比他小的,右邊放比他大的 """ tmp = li[left] # 第一個位置 while left < right: # 一直迴圈 while left < right and li[right] >= tmp: # 右邊開始找比tmp 小的數放到左邊的空位 right -= 1 # 往左走一步 li[left] = li[right] # 把右邊的值寫到左邊空位 # 有可能所有數都比自己大
while left < right and li[left] <= tmp: left += 1 li[right] = li[left] # 把左邊的值寫到右邊的空位上 li[left] = tmp # 把tmp歸位 return left # 或者返回right都行 li_test = [3, 2, 7, 1, 6, 9, 8, 4] quick_sort(li_test, 0, len(li_test)-1) print(li_test)

堆排序 ★★

演算法複雜度: O(nlogn) <n乘logn>

前提

二叉樹的知識儲備!!

效率:

快排的時間複雜度優於堆排

def sift(li, low, high):
    """
    調整堆
    :param li: 列表
    :param low: 堆的根節點
    :param high: 堆的最後一個元素位置
    :return:
    """
    i = low  # i最開始指向的父
    j = 2 * i + 1  # 堆頂左孩子
    tmp = li[low]  # 把堆頂存起來
    while j <= high:  # 只要j位置有數
        if j + 1 <= high and li[j + 1] > li[j]:  # 如果右還在比較大且右孩子有
            j = j + 1  # j指向右孩子
        if li[j] > tmp:
            li[i] = li[j]
            i = j  # 往下一步看
            j = 2 * i + 1
        else:  # tmp更大,把tmp放到i位置上
            li[i] = tmp  # 把tmp放到某一級領導位置上
            break
    else:
        li[i] = tmp  # 把tmp放到葉子節點上


def heap_sort(li):
    """
    堆排序
    """
    n = len(li)
    for i in range((n-2)//2, -1, -1):
        # i表示建堆時調整的部分的根下標
        sift(li, i, n-1)
    # 建堆完成了
    for i in range(n-1, -1, -1):
        # i向當前堆最後一個元素
        li[0], li[i] = li[i], li[0]
        sift(li, 0, i-1)  # i-1是新的high


li_test = [3, 2, 7, 1, 6, 9, 8, 4]
heap_sort(li_test)
print(li_test)

堆排序py 模組

import heapq  # q : queue 優先列隊
import random

li = list(range(100))

random.shuffle(li)  # 打亂

print(li)

heapq.heapify(li)  # 建堆

n = len(li)
for i in range(n):
    print(heapq.heappop(li), end=',')

堆排序 topk問題 【常用】

問題:比如熱搜網,有n個數,取前k打的數(排序好的)

思路

  1. 取列表前k個元素建立小根堆,堆頂是目前第k大的數
  2. 依次向後面遍歷原列表,對於列表中的元素,如果小於堆頂,則忽略該元素,反之換為該元素,並進行一次調整
  3. 遍歷素有元素後,倒序彈出堆頂
import random


def sift(li, low, high):
    """
    調整為小根堆
    :param li: 列表
    :param low: 堆的根節點
    :param high: 堆的最後一個元素位置
    :return:
    """
    i = low  # i最開始指向的父
    j = 2 * i + 1  # 堆頂左孩子
    tmp = li[low]  # 把堆頂存起來
    while j <= high:  # 只要j位置有數
        if j + 1 <= high and li[j + 1] < li[j]:  # 如果右還在比較大且右孩子有
            j = j + 1  # j指向右孩子
        if li[j] < tmp:
            li[i] = li[j]
            i = j  # 往下一步看
            j = 2 * i + 1
        else:  # tmp更大,把tmp放到i位置上
            li[i] = tmp  # 把tmp放到某一級領導位置上
            break
    else:
        li[i] = tmp  # 把tmp放到葉子節點上


def top_key(li, k):
    heap = li[0:k]
    for i in range((k-2)//2, -1, -1):
        sift(heap, i, k-1)
    # 1.建堆
    for i in range(k, len(li)-1):
        if li[i] > heap[0]:
            heap[0] = li[i]
            sift(heap, 0, k-1)
    # 2.遍歷
    for i in range(k-1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i-1)
    # 3.出數
    return heap


li = list(range(1000))
random.shuffle(li)


print(top_key(li, 10))  # 測試 前10數

歸併排序 ★★

演算法複雜度: O(nlogn) <n乘logn>
空間複雜度: O(n)

思路

假設兩個列表已有序,那麼將他們合併在一起,將列表越分越小,直到分為一個元素

def merge(li, low, mid, high):
    """
    :param li: 列表
    :param low: 左列表第一個元素
    :param mid: 左列表最後一個元素,那麼右列表第一個就是mid+1
    :param high: 右列表最後一個元素
    :return:
    """
    i = low  # 第一段第一個元素
    j = mid + 1  # 第二段的第一個元素
    tmp_list = []
    while i <= mid and j <= high:  # 必須左右有數
        if li[i] < li[j]:
            tmp_list.append(li[i])
            # 移動後箭頭必須移動一位,因為已經把小的值提出到臨時list中
            i += 1
        else:
            tmp_list.append(li[j])
            j += 1
    # while 執行完,說明有一段執行完了,剩下的接到tmp中即可
    # 分別判斷一下那個還有數
    while i <= mid:
        tmp_list.append(li[i])
        i += 1
    while j <= high:
        tmp_list.append(li[j])
        j += 1
    li[low:high+1] = tmp_list  # 寫回去


def merge_sort(li, low, high):
    if low < high:  # 至少有2個元素,遞迴
        mid = (low + high) // 2  # 整除2
        merge_sort(li, low, mid)
        merge_sort(li, mid+1, high)
        merge(li, low, mid, high)


li_test = [3, 2, 7, 1, 6, 9, 8, 4]
merge_sort(li_test, 0, len(li_test)-1)
print(li_test)