本文主要使用python來實現七個經典的排序算法,分別是:冒泡排序、選擇排序,插入排序,快速排序,希爾排序,堆排序和歸併排序。
一、相關歸納總結
1、時間複雜度
O(N^2): 冒泡排序、選擇排序,插入排序
O(N*logN): 快速排序,希爾排序,堆排序和歸併排序
2、空間複雜度
O(1):插入排序,冒泡排序,選擇排序,堆排序,希爾排序
O(logN)~O(N):快速排序
O(N):歸併排序
3、穩定性:若待排序的序列中,存在多個相同關鍵字的記錄,經過排序,這些記錄的相對次序保持不變,則稱該算法是穩定的;若經過排序後,記錄的相對次序發生了改變,則稱該算法是不穩定的。
穩定的:冒泡排序,插入排序、歸併排序和基數排序
不穩定的:選擇排序,快速排序,希爾排序,堆排序
不穩定舉例說明:
選擇排序:
---->
快速排序:
希爾排序:
---->
堆排序:
4、使用場景:
設待排序的元素個數爲n:
當n較大,則應採用時間複雜度爲O(NlogN)的排序算法:快速排序,堆排序或者歸併排序
- 快速排序:是目前基於比較的內部排序中被認爲是最好的方法,當待排序的關鍵字是隨機分佈時,快速排序的平均時間最短。
- 堆排序:如果內存空間允許
- 歸併排序:內存空間允許,且要求穩定性
當n較小,可採用直接插入和選擇排序
- 直接插入排序:當元素分佈有序,且要求穩定性(優先)
- 選擇排序:當元素分佈有序,且不要求穩定性
一般不使用或不直接使用傳統的冒泡排序。
二、算法實現
1、冒泡排序:
基本思想:
假設待排序表長爲n,從前往後(或從後往前)兩兩比較相鄰元素的值,若爲逆序(即A[i]>A[i+1]),則交換他們,直到序列比較完。我們稱它爲一趟冒泡,會將最大的元素交換到待排序的最後一個位置。下一趟冒泡時,前一趟確定的最大元素不再參加比較,待排序列減少一個元素,每趟冒泡的結果把序列中的最大元素方法了序列的最終位置。這樣最多做n-1趟冒泡就能把左右的元素排好序。
實現代碼:
def bubble_sort(list):
for i in range(len(list)-1):
for j in range(len(list)-i-1):
if list[j]>list[j+1]:
list[j+1],list[j] = list[j],list[j+1]
return list
2、選擇排序:基本思想:
每一趟(例如第i趟)在後面n-i+1(i=1,2,...,n-1)個待排序元素中選取關鍵字最小的元素,作爲有序子序列的第i個元素,直到n-1趟做完,就不用再選了。
實現代碼
def select_sort(list):
for i in range(len(list)-1):
min = i
for j in range(i+1,len(list)):
if list[j]<list[min]:
min = j
list[i],list[min] = list[min],list[i]
return list
3、插入排序基本思想:
將一個記錄插入到一排序好的有序表中,從而得到一個新的,記錄數增1的有序表。即:現將序列的第一個記錄看成是一個有序的子序列,然後從第二個記錄進行插入,直至整個序列有序爲止。
實現代碼:
def insert_sort(list):
for i in range(1,len(list)):
key = list[i]
for j in range(i-1,-1,-1):
if list[j]>key:
list[j+1] = list[j]
list[j] = key
return list
4、快速排序基本思想:
- 選擇一個基準元素,通常選擇第一個元素或者最後一個元素
- 通過一趟排序將待排序的記錄分割成獨立的兩部分,其中一部分的記錄值均比基準元素小,另一部分元素值均比基準元素大
- 此時基準元素在其排好序後的正確位置
- 然後分別堆這兩部分用同樣的方法繼續進行排序,直到整個序列有序
實現代碼:
def quick_sort(list,left,right):
if left<right:
mid = partition(list,left,right)
quick_sort(list,0,mid-1)
quick_sort(list,mid+1,right)
return list
def partition(list,left,right):
temp = list[left]
while left<right:
while left<right and list[right]>=temp:
right -= 1
list[left] = list[right]
while left<right and list[left]<=temp:
left += 1
list[right] = list[left]
list[left] = temp
return left
5、希爾排序
希爾排序也叫縮小增量排序
基本思想:
- 首先取一個正數d1 = n/2,將元素分爲d1個組,每組相鄰元素之間的距離爲d1,在各組內進行直接插入排序
- 取第二個正數d2 = d1 / 2,重複上述分組排序過程,直到di = 1,即所有的元素都在同一組進行直接插入排序。
- 希爾排序每趟並不使某些元素有序,而是使整體數據越來越接近有序,最後一趟排序使得所有數據有序
實現代碼:
def shell_sort(list):
#dk爲步長
dk = len(list)/2
while dk>=1:
for i in range(dk,len(list)):
temp = list[i]
j = i-dk
while j>=0 and temp<list[j]:
list[j+dk] = list[j]
j -= dk
list[j+dk] = temp
dk = dk/2
return list
6、堆排序
堆排序是一種樹形選擇排序方法
基本思想
- 建立堆
- 得到堆頂元素,爲最大元素
- 去掉堆頂元素,將堆最後一個元素放到堆頂,此時可通過一次調整重新使堆有序
- 堆頂元素爲第二大元素
- 重複步驟3,直到堆變空
實現代碼:
#堆排序
def heap_sort(list):
#初始建堆
build_max_heap(list,len(list))
#n-1趟的交換和建堆過程
for i in range(1,len(list))[::-1]:
#將堆頂元素list[0]和最後一個元素list[i]交換
list[i],list[0] = list[0],list[i]
#把剩餘的i-1個元素整理成堆
adjust_down(list,0,i)
return list
#創建堆
# n個節點的完全二叉樹,最後一個節點是第n/2個節點的孩子。
#對於大根堆,若根節點的關鍵字小於左右子女中關鍵字較大者,則交換,使該子樹成爲堆。
#之後向前依次對各節點((size/2)-1~1)爲根的子樹進行篩選。
def build_max_heap(list,size):
#從size/2~1,反覆調整堆。
for i in range(0,size/2)[::-1]:
adjust_down(list,i,size)
#調整堆
def adjust_down(list,i,size):
max= i
lchild = 2*i + 1
rchild = 2*i + 2
if rchild < size :
if lchild < size and list[lchild] > list[max]:
max = lchild
if rchild < size and list[rchild] > list[max]:
max = rchild
if max!= i:
list[max],list[i] = list[i],list[max]
#繼續向下調整堆
adjust_down(list,max,size)
7、歸併排序
基本思想:
歸併排序是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每個子序列是有序的。然後再把有序子序列合併爲整體有序序列
圖例:
實現代碼:
def merge_sort(list):
if len(list)<=1:
return list
mid = len(list)/2
left = merge_sort(list[:mid])
right = merge_sort(list[mid:])
return merge(left,right)
def merge(left,right):
i,j = 0,0
#新建一個數組,用來存儲將left和right排好序的值
result = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result