排序演算法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