排序算法
1. 冒泡排序
冒泡排序,從左到右,分別比較兩個相鄰數字的大小,如果後面一個數小於前面的數就進行交換。
def bubble_sort(seq):
l = len(seq)
for i in range(l-1):
for j in range(l-1-i):
if seq[j] > seq[j+1]:
seq[j],seq[j+1] = seq[j+1],seq[j]
print(seq)
輸入一個list: A = [5, 6, 3, 4, 1, 0, 2]
結果:
可以看到一輪輪下來大的數字被慢慢往後移。
2. 選擇排序
每次從序列中找到最小的數插入序列的迭代位置。這樣排序結束就是從小到大。
def select_sort(seq):
l = len(seq)
for i in range(l-1):
min_val = i
for j in range(i+1,l):
if seq[j]<seq[min_val]:
min_val = j
if min_val!= i:
seq[min_val],seq[i] = seq[i],seq[min_val]
print(seq)
相同的list:
結果:
3. 插入排序
假設從第一個元素開始,每一個元素都是新插進來的,那麼每次插進的元素和之前的比較,放入該放的地方即可。
def insert_sort(seq):
l = len(seq)
for i in range(1,l):
val = seq[i]
pos = i
while val < seq[pos-1] and pos > 0:
seq[pos] =seq[pos-1]
pos-=1
seq[pos] = val
print(seq)
結果:
4.歸併排序
基本思想就是:
- 分解:將待排序的 n 個元素分成各包含 n/2 個元素的子序列
- 解決:使用歸併排序遞歸排序兩個子序列
- 合併:合併兩個已經排序的子序列以產生已排序的答案
# 用遞歸的思想來解決:
# 首先找到中間的數字作爲基準,在左右兩邊分別進行排序,最後將兩個有序的序列進行合併:
def merge_sort(seq):
if len(seq) <= 1:
return seq
else:
mid = int(len(seq)/2)
left = merge_sort(seq[:mid])
right = merge_sort(seq[mid:])
new_seq = merge_sort_list(left,right)
return new_seq
# 此函數用來合併兩個已經排序好的list
# 定義兩個指針,將左右排序好的list依次放入最後的list裏形成一個有序的最終數組
def merge_sort_list(left,right):
new_seq = []
a = b =0
while a < len(left) and b < len(right):
if left[a] < right[b]:
new_seq.append(left[a])
a += 1
else:
new_seq.append(right[b])
b += 1
if a < len(left):
new_seq.extend(left[a:])
else:
new_seq.extend(right[b:])
return new_seq
進行測試:
seq =[10,23,51,18,4,31,13,5]
merge_sort(seq)
# result: [4, 5, 10, 13, 18, 23, 31, 51]
時間複雜度爲O(nlogn)
5. 快速排序
快速排序是比較重要以及常用的一種排序方法。基本思想爲:
- 選擇基準值 pivot 將數組分成兩個子數組:小於基準值的元素和大於基準值的元素。這個過程稱之爲 partition
- 對這兩個子數組進行快速排序。
- 合併結果
def quick_sort(seq):
if not seq or len(seq) <=1:
return seq
else:
pivot_idx = 0
l_seq = [seq[i] for i in range(len(seq)) if seq[i] <= seq[pivot_idx] and pivot_idx != i] # 特別注意要加上 pivot_idx != i,不然會出現死循環
r_seq = [seq[i] for i in range(len(seq)) if seq[i] > seq[pivot_idx]and pivot_idx != i]
return(quick_sort(l_seq)+[seq[pivot_idx]] +quick_sort(r_seq))
上面的快排遍歷了數組兩次:
1. 需要額外存儲; 2. partition每次都遍歷了整個數組
我們想實現原地排序可以使用這種方法:
設置首位倆個指針 left, right,兩個指針不斷向中間收攏。如果遇到 left 位置的元素大於 pivot 並且 right 指向的元素小於 pivot,我們就交換這倆元素,當 left > right 的時候退出就行了,這樣實現了一次遍歷就完成了 partition。
def quicksort_inplace(array, beg, end): # 左閉右開區間,end 傳入 len(array)
if beg < end: # beg == end 的時候遞歸出口
pivot = partition(array, beg, end)
quicksort_inplace(array, beg, pivot)
quicksort_inplace(array, pivot+1, end)
# 實現partition操作:
def partition(array, beg, end):
pivot_index = beg
pivot = array[pivot_index]
left = pivot_index + 1
right = end - 1 # 開區間,最後一個元素位置是 end-1
while True:
# 從左邊找到比 pivot 大的
while left <= right and array[left] < pivot:
left += 1
while right >= left and array[right] >= pivot:
right -= 1
if left > right:
break
else:
array[left], array[right] = array[right], array[left]
array[pivot_index], array[right] = array[right], array[pivot_index]
return right # 新的 pivot 位置