排序演算法的python實現
本文所有的排序方法都在列表上進行操作,首先定義交換任意兩項位置的函式 swap 。
def swap (x,i,j):
"""
交換x的i,j位置元素
"""
temp = x[i]
x[i] = x[j]
x[j] = temp
1、選擇排序
排序演算法的邏輯非常簡單,首先搜尋整個列表,找到最小項的位置,如果該位置不是列表的第1項,就交換這兩個位置的元素。然後從列表的第2個元素開始,重複上述過程,直到演算法達到整個過程的最後一個位置,圖形解釋如下
程式碼如下
def selectionSort (x):
i = 0
while i < len(x) - 1 :
minindex = i
j = i + 1
while j < len(x) :
if x[minindex] > x[j]:
minindex = j
j+= 1
if minindex != i:
swap(x,i,minindex)
i+= 1
return x
函式包括一個巢狀的迴圈,對於大小為n的列表,外圍的迴圈執行 n-1 次,內部迴圈的次數從 n-1 遞減到 1 ,因此,選擇排序在各種情況下的複雜度為平方階,執行結果如下
2、二元選擇排序法(選擇排序改進)
選擇排序法每輪只找最小值,效率較低,可以考慮每次同時尋找最小值和最大值,並且在某一輪如果最小值與最大值相同,說明剩下的數字都相同,可以直接結束。
此外,與選擇排序不同的是,需要考慮到如果第i輪裡,恰好第i個數就是最大值時,先交換 minindex 和 i 之後,最大值的下標變成了 minindex ,這時候應該交換 minindex 和 n-i-1 ,而不是 maxindex 和 n-i-1 。程式碼如下
def selectionSort_1( x ):
i = 0
while i <= len ( x ) // 2 :
minindex = i
maxindex = i
j = i + 1
while j < len ( x ) - i :
if x [minindex] > x [ j ]:
minindex = j
if x [maxindex] < x [ j ]:
maxindex = j
j += 1
if x [minindex] == x [maxindex]:
return x
if minindex != i:
swap( x ,i,minindex)
if maxindex != len ( x ) - 1 - i :
if maxindex != i:
swap( x , len ( x ) - 1 - i,maxindex)
else :
swap( x , len ( x ) - 1 - i, minindex)
i+= 1
return x
3、氣泡排序
氣泡排序從列表的開頭處開始,逐個比較一對資料項,如果順序不對就交換兩項位置,直到移動到列表的末尾。這個過程的效果就是將最大的項以冒泡的方式排到演算法的末尾。然後演算法從列表開頭到倒數第二項重複這一過程,依次類推,圖形解釋如下。
程式碼如下
def BubbleSort (x):
j = len(x) - 1
while j > 0 :
i = 0
while i < j:
if x[i] > x[i + 1 ]:
swap(x,i,i+ 1 )
i += 1
j -= 1
return x
類似選擇排序,氣泡排序也是一個巢狀的迴圈,如果列表是已經排好序的,氣泡排序不會執行任何的交換,在最壞的情況下,為平方階複雜度。
4、氣泡排序法改進
在最好的情況下,氣泡排序法依然會執行每個迴圈但不進行任何操作,可以設定一個標記判斷氣泡排序法在一次內層迴圈中是否進行了交換,如果沒有,說明演算法已經使排好序的,就可以直接返回,不過這種方法只是對最好的情況進行了改進,程式碼如下
def BubbleSort_1 (x):
j = len(x) - 1
while j > 0 :
flag = False
i = 0
while i < j:
if x[i] > x[i + 1 ]:
swap(x,i,i+ 1 )
flag = True
i += 1
if not flag:
return x
j -= 1
return x
5、雙向氣泡排序法
氣泡排序法存在所謂的“烏龜問題”,假設我們需要將序列按照升序排序。序列中的較小的數字又大量存在於序列的尾部,這樣會讓小數字在向前移動得很緩慢,因此針對這一問題,產生了雙向氣泡排序法,也稱雞尾酒排序法。
雙向氣泡排序法由兩個方向同時進行冒泡,首先由左向右為大元素移動方向,從右向左為小元素移動方向,然後每個元素都依次執行。在第i次移動後,前i個和後i個元素都放到了正確的位置。圖形解釋如下
程式碼如下
def BidirectionalBubbleSort (x):
j = 0
while j <= len(x)// 2 :
flag = False
for i in range(j ,len(x) - j - 1 ):
if x[i]>x[i+ 1 ]:
swap(x,i,i+ 1 )
flag= True
for i in range(len(x)- 1 - j,j, -1 ):
if x[i]<x[i -1 ]:
swap(x,i,i -1 )
flag= True
if not flag:
return x
j += 1
return x
實驗結果如下
6、插入排序法
插入排序法類似打牌時候摸撲克牌整理順序的過程,邏輯如下:
● 在第 i 輪通過列表的時候( i 從 1 到 n-1 ),第i項應該插入到列表的前i個項中的正確位置;
● 在第 i 輪之後,前 i 個項應該是排好序的;
圖形解釋如下
程式碼如下
def InsertionSort (x):
i = 1
while i < len(x):
j = i - 1
item = x[i]
while j >= 0 and item < x[j]:
x[j + 1 ] = x[j]
j -= 1
x[j + 1 ] = item
i += 1
return x
7、希爾排序(插入排序改進)
插入排序對於幾乎已經排好序的資料操作時,效率很高,但平均來說,插入排序很低效,因為插入排序每次只能將資料移動一位,希爾排序是在此基礎上對於插入排序的一種改進。
希爾演算法的邏輯是,先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序,具體步驟如下:
● 設定一個較大間隔 gap ,對所有間隔為 gap 的資料通過插入排序法進行排序;
● 執行完之後根據某種邏輯縮小 gap ( 程式碼中採用除以3取整的辦法 ),重複上述過程,直到 gap = 0 。
通過以上步驟,最終得到的列表是排好序的,並且可以證明,這種方法的平均的複雜度是 O(nlogn) 。程式碼如下
def HashSort( x ):
gap = round ( len ( x )* 2 / 3 )
while gap > 0 :
print ( 'gap = ' ,gap )
i = gap
while i < len ( x ):
j = i - gap
item = x [i]
while j >= 0 and item < x [ j ]:
x [ j + gap] = x [ j ]
j -= gap
x [ j + gap] = item
i += 1
gap = round (gap/ 3 )
return x
這裡 print 每次迴圈中 gap 的值,可以直觀看到 gap 的變化過程,實驗如下
關於各種方法效率的比較不再說明,其餘排序方法下期再見,程式碼有問題的地方請指出!
原文釋出時間為:2018-11-28
本文作者: 量化小白H
本文來自雲棲社群合作伙伴“ ofollow,noindex">Python愛好者社群 ”,瞭解相關資訊可以關注“ Python愛好者社群 ”。