1. 程式人生 > >排序演算法python

排序演算法python

演算法概述

演算法效能

任何基於比較的演算法時間複雜度上不會優於NlgN,低於這一值說明演算法未能覆蓋所有的情況。

演算法 穩定性 時間複雜度 空間複雜度
氣泡排序 穩定 N2 1
選擇排序 不穩定 N2 1
插入排序 穩定 N~N2 1
希爾排序 不穩定 NlgN? N1.2
?
1
歸併排序 穩定 NlgN N
快速排序 不穩定 NlgN lgN
堆排序 不穩定 NlgN 1
計數排序 穩定 N N
基數排序 穩定 N N

目前不能準確地描述希爾演算法的時間複雜度,但在大量的實驗中發現它的效能要好於普通的插入排序。

排序演算法穩定性的重要性

演算法的穩定性指在經過排序後能否保留重複元素的相對位置。這種性質對於處理具有多個鍵值的資料時很重要,例如在按第一個鍵值排序資料後,我們需要再按第二個鍵值分類資料時,穩定的演算法能保證分類後的資料組依然保持第一個鍵值的有序性。

排序演算法Python實現

氣泡排序

每次比較相鄰的兩個數,總是將較大(或者較小)的數放到最後(或者最前)。

def bubble_sort(l1):
 for i in range(len(l1)):
  for j in range(len(l1)-i-1):
   if l1[j] > l1[j+1]:
    l1[j] += l1[j+1]
    l1[j+1] = l1[j] - l1[j+1]
    l1[j] -= l1[j+1]
 return l1

選擇排序

即是每次從序列中搜索出最大(小)的值,然後放到序列最前(後)。

def selection_sort(l1):
 for i in range(len(l1)):
  for j in range(i,len(l1)):
   if l1[j] <= l1[i]:
    temp = l1[i]
    l1[i] = l1[j]
    l1[j] = temp
 return l1

插入排序

def insertion_sort(l1):
 for i in range(1, len(l1)):
  temp = l1[i]
  for j in range(i-1, -1, -1):
   if l1[j] > temp:
    l1[j+1] = l1[j]
   else:
    l1[j+1] = temp
    break
 return l1   

希爾排序

希爾排序作為插入排序的變形,為什麼能比插入排序快呢?要知道每次我們進行插入時是在尋找到合適位置後就停止搜尋,而如過有些元素需要從數列最前端移動到最後(或者反過來)的話,這些元素
將一步一步移動然後花費大量時間。而希爾排序的祕訣就在於讓部分序列有序化以達到提前停止搜尋的作用,因此更省時間。也就是說先通過大步移動讓較大的數到數列後端,較小的數到數列前端,以保證在最後一步一步排序時只需要移動較短距離即可達到有序。

def shell_sort(list1):
    h = 1
    length = len(list1)
    while h < length//3:
        h = 3*h + 1
    while h >= 1:
        for idx in range(h):
            for i in range(idx+h, length, h):
                temp = list1[i]
                for j in range(i-h, -1, -h):
                    if list1[j] > temp:
                        list1[j+h] = list1[j]
                        if j < h:
                            list1[j] = temp
                    else:
                        list1[j+h] = temp
                        break
        h = h//3
    return list1

歸併排序

def merge_sort(list1):
    length = len(list1)
    if length > 3:
        midl = length // 2
        sublist1 = merge_sort(list1[0:midl])
        sublist2 = merge_sort(list1[midl:length])
        len1 = len(sublist1)
        len2 = len(sublist2)
        m = 0
        n = 0
        while m < len1 and n < len2:        # merge two sublists
            if sublist1[m] < sublist2[n]:
                list1[m+n] = sublist1[m]
                m += 1
            else:
                list1[m+n] = sublist2[n]
                n += 1
        if m == len1:
            list1[(m+n):length] = sublist2[n:len2]
        else:
            list1[(m+n):length] = sublist1[m:len1]            
    else:    # minimal length, do not divide list
        for i in range(length-1):
            for j in range (length-i-1):
                if list1[j] > list1[j+1]:
                    temp = list1[j]
                    list1[j] = list1[j+1]
                    list1[j+1] = temp
    return list1

快速排序

def quick_sort(list1):
    len(list1)
    pivot_index = 0
    tail_index = length - 1    # In order to reduce exchanging operations, 
                               # put the bigger element to the tail of the list
    if length >= 3:
        piv = list1[pivot_index]
        for i in range(1, length):
            if piv >= list1[pivot_index+1]:
                list1[pivot_index] = list1[pivot_index+1]
                pivot_index += 1
            else:
                temp = list1[pivot_index+1]
                list1[pivot_index+1] = list1[tail_index]
                list1[tail_index] = temp
                tail_index -= 1
        list1[pivot_index] = piv
        list1 = (quick_sort(list1[0:pivot_index]) + [list1[pivot_index],] +
                quick_sort(list1[pivot_index+1:length]))
    else:
        for i in range(length-1):
            if list1[i] >= list1[i+1]:
                temp = list1[i]
                list1[i] = list1[i+1]
                list1[i+1] = temp
    return list1

堆排序

堆排序是應用了優先佇列的思想,每次比較部分陣列,取出最大值,然後重複依次取出剩餘序列中的最大值。演算法的實現主要分兩個步驟。首先初始化堆使得堆有序,然後交換最後一個節點與第一節點並從二叉樹中刪除最後一個節點(取出最大值或者最小值),使用下沉排序有序化剩餘二叉樹節點。重複直至所有節點從二叉樹中移除

def heap_sink(list1, parent_node):
    length = len(list1)
    last_parent = length // 2
    while parent_node <= last_parent:
        if 2*parent_node+1 <= length:
            if list1[2*parent_node] > list1[2*parent_node-1]:    # refer to 2i+1 and 2i elements
                larger_child = 2*parent_node + 1 
            else:
                larger_child = 2*parent_node
        else:
            larger_child = 2*parent_node
        if list1[parent_node-1] < list1[larger_child-1]:
            temp = list1[larger_child-1]
            list1[larger_child-1] = list1[parent_node-1]
            list1[parent_node-1] = temp
            parent_node = larger_child
        else:
            break;
    return list1


def heap_sort(list1):
    length = len(list1)
    # First step: sort heap
    last_parent = length // 2
    for i in range(last_parent, 0, -1):
        list1 = heap_sink(list1, i)
    # Second step: sink sort
    for i in range(length, 1, -1):
        temp = list1[i-1]
        list1[i-1] = list1[0]
        list1[0] = temp
        list1[0:i-1] = heap_sink(list1[0:i-1], 1)
    return list1

基數排序

演算法原理大概是依次按照每一個基數排序,實現部分有序化並最終實現序列有序。比如LSD演算法就是先按最低位排序,在低位數有序的情況下繼續對高位數進行排序,因為演算法本身是穩定的,所以最終能達到所有位數都有序也就是數列有序化。
有點類似計數排序,桶排序,但是實用性更高。相比於計數排序,使用的空間更少,因此適用性更強,不必規定陣列處於一定範圍內。

def radix_sort(list1):
    max_value = list1[0]    
    radix = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
    for i in list1:    # sort by the smallest radix and find max value
        if i > max_value:
            max_value = i
        remainder = i % 10
        radix[remainder].append(i)
    bit_scale = 10
    while max_value//bit_scale > 0:    # sort by the remain radix
        new_radix = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
        for i in radix:
            for j in radix[i]:
                remainder = j//bit_scale % 10
                new_radix[remainder].append(j)
        radix = new_radix
        bit_scale *= 10
    sorted_list = []
    for i in radix:
        sorted_list += radix[i]
    return sorted_list

參考資料

  1. python官方文件
  2. 十大經典排序演算法(動圖演示)