1、常用排序算法
排序算法 | 平均時間 | 最差情形 | 穩定度 | 備注 |
---|---|---|---|---|
快速排序 | O(nlogn) | O(n^2) | 不穩定 | n大時較好 |
冒泡排序 | O(n^2) | O(n^2) | 穩定 | n小時較好 |
選擇排序 | O(n^2) | O(n^2) | 不穩定 | n小時較好 |
插入排序 | O(n^2) | O(n^2) | 穩定 | 大部分已排好序時較好 |
歸并排序 | O(nlogn) | O(nlogn) | 穩定 | n大時較好 |
希爾排序 | O(nlogn) | O(n^s) 1<s<2 | 不穩定 | s是所選分組 |
2、快速排序法
基本思想:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。注:快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變動。
-
一趟快速排序算法(首先任意選取一個數據(通常選用數組的第一個數)作為關鍵數據,然后將所有比它小的數都放到它前面,所有比它大的數都放到它后面)步驟:
1)設置兩個變量i、j,排序開始的時候:i=0,j=N-1(列表長度最后一個元素下標);
2)以第一個列表元素作為關鍵數據,賦值給key,即key=A[0];
3)從j開始向前搜索,即由后開始向前搜索(j-=1),找到第一個小于key的值A[j],將A[j]和A[i]互換;
4)從i開始向后搜索,即由前開始向后搜索(i+=1),找到第一個大于key的A[i],將A[i]和A[j]互換;
5)重復第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小于key,4中A[i]不大于key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j位置不變。另外,i=j這一過程一定正好是i+或j-完成的時候,此時令循環結束)。
-
示例:
假設用戶輸入了如下數組:
下標 0 1 2 3 4 5 數據 6 2 7 3 8 9 創建變量i=0(指向第一個數據), j=5(指向最后一個數據), key=6(賦值為第一個數據的值)。
(1)要把所有比key小的數移動到key的左面,故先開始尋找比6小的數,從j開始,從右往左找,不斷遞減變量j的值(j-=1),找到第一個下標3的數據比6小,于是把數據3移到下標0的位置(A[i]=A[j]),把下標0的數據6移到下標3,完成第一次比較:
下標 0 1 2 3 4 5 數據 3 2 7 6 8 9 (2)i=0,j=3,key=6,第二次比較(通俗來說:移動指針從j開始的,而因為第一個比較時,找到了值比key小,故該值由key的右邊移到了左邊,稱做發生了“交叉變換”行為,故移動指針變為i了,要去找比key大的值了;說明:之后每發生一次所謂的“交叉變換”,移動指針都要變化的),移動指針為i,從前往后找,遞加變量i,發現下標2的數據是第一個比key大的,于是用下標2的數據7和j指向的下標3的數據的6做交換,數據狀態變成下表:
下標 0 1 2 3 4 5 數據 3 2 6 7 8 9 (3)i=2,j=3,key=6,移動指針變為j,故遞減變量j,不斷重復進行上面的循環比較。
(4)直到i=j:都指向了下標2。于是,第一遍比較結束。得到結果如下,凡是key(=6)左邊的數都比它小,凡是key右邊的數都比它大:
下標 0 1 2 3 4 5 數據 3 2 6 7 8 9 如果i和j沒有碰頭的話,就遞加i找大的,還沒有,就再遞減j找小的,如此反復,不斷循環。注意判斷和尋找是同時進行的。然后,對key兩邊的數據,再分組分別進行上述的過程,直到不能再分組為止。
注意:第一遍快速排序不會直接得到最終結果,只會把比key大和比key小的數分到key的兩邊。為了得到最后結果,需要再次對下標2兩邊的數組分別執行此步驟,然后再分解數組,直到數組不能再分解為止(只有一個數據),才能得到正確結果。
-
Python代碼
def quickSort_main(data_list,i,j):#快速排序法主調用程序,調用開始時i=1,j=len(data_kist)-1 if i < j: split_point = quickSort_spec(data_list,i,j) quickSort_main(data_list,i,split_point) #遞歸 quickSort_main(data_list,split_point+1,j) def quickSort_spec(data_list,i,j):#快速排序具體過程 key = data_list[i] while i < j: while i < j and data_list[j]>=key: j-=1 data_list[i] = data_list[j]#不在循環里了,即data_list[j]<key,要將j下標對應的值放到i位置 while i < j and data_list[i]<=key: i +=1 data_list[j] = data_list[i]#不在循環里了,即data_list[i]>key,要將i下標對應的值放到j位置 data_list[i] = key #此時i=j,即找到了key要放的位置 return i #返回key所在下標位置 >>>data_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] >>>quickSort_main(data_list,0,len(data_list)-1) >>>print(data_list) [17, 20, 26, 31, 44, 54, 55, 77, 93]
3、冒泡排序法
-
基本思想
每個回合都從第一個元素開始和它后面的元素比較,如果比它后面的元素更大的話就交換,一直重復,直到這個元素到達了所進行排序元素的最后位置。繼續重復操作,每次遍歷都會將剩下的元素中最大的那個放到了序列的“最后”(除去了前面已經排好的那些元素),即排序一次后大的元素會經由交換慢慢“浮”到數列的頂端(冒泡命名的來源)。算法時間復雜度為O(n^2)
-
步驟
(1)比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
(2)對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元素應該會是最大的數。
(3)針對所有的元素重復以上的步驟,除了最后一個。
(4)持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
-
示例
冒泡排序上圖為一輪排序,可見一輪排序后,最大的元素93會"浮"到頂端,下一輪排序則對(26,54,17,77,31,44,55,20)進行,以此重復,直到沒有元素
-
Python代碼
def bubble_sort(data_list):#冒泡排序法 length = len(data_list) exchanges = True while length > 0 and exchanges: exchanges = False for i in range(length-1): if data_list[i]>data_list[i+1]: exchanges = True #python中可直接交換兩個變量,而不需引入臨時變量temp來交換 data_list[i],data_list[i+1] = data_list[i+1],data_list[i] length-=1 #一輪排序后,已放置好的那個元素不需要再參與下一輪的排序 >>>data_list = [5, 3, 7, 9, 6, 1, 10] >>>bubble_sort(data_list) >>>print(data_list) [1, 3, 5, 6, 7, 9, 10] #增加exchanges作為標識,即如果有一趟排序中沒有產生交換的話,那么說明此刻數列以及變成了有序的數列,如:5,1,2,3,4,冒泡一次之后就變成了:1,2,3,4,5,已經有序了,我們沒有必要再去比較了。
4、選擇排序法
-
基本思想
每個回合都選擇出剩下的元素中最小(大)的那個,選擇的方法是首先默認第一元素是最小(大)的,如果后面的元素比它小(大)的話,那就更新當前的最小(大)的元素值,直到找到最后,把當前參與排序中的第一個元素和找到的那個最小(值)所在的位置交換。以此重復排序,直到排序完成。時間復雜度O(n^2),選擇排序法是不穩定排序算法。
-
步驟
(1)在待排序記錄A[1]~A[n]中選出最小的記錄,將它與A[1]交換
(2)在待排序記錄A[2]~A[n]中選出最小的記錄,將它與A[2]交換
(3)以此類推,第i趟在待排序記錄A[i]~A[n]中選出最小的記錄,將它與A[i]交換,使有序序列不斷增長直到全部排序完畢。
-
示例
初始序列:{49 27 65 97 76 12 38} ,其中大括號內為無序區,大括號外為有序序列
第1趟:先假設第一個元素49是最小的,然后不停往后比較,找到12是當前最小的元素,則將12與49交換:12{27 65 97 76 49 38}
第2趟:在剩余元素中假設27最小的,不停查找到最后,27還是最小的,故保持27不動 :12 27{65 97 76 49 38}
第3趟:方法同上,此處是65與38交換:12 27 38{97 76 49 65}
第4趟:方法同上,此處是97與49交換:12 27 38 49{76 97 65}
第5趟:方法同上,此處是76與65交換:12 27 38 49 65{97 76}
第6趟:方法同上,此處是97與76交換:12 27 38 49 65 76 97 完成
-
Python代碼
def select_sort(data_list):#選擇排序法 length = len(data_list) for i in range(length): min_index = i #記錄最小元素的下標(每次都是把參與排序的第一個元素先作為最小值) for j in range(i+1,length): #在i+1開始一直往后查找最小值 if data_list[j] < data_list[min_index]: min_index = j #找到了更小的值,故當前最小元素下標變為了j if min_index != i: #min_index=i時,是不用交換的 data_list[i],data_list[min_index] = data_list[min_index],data_list[i] #每次交換,都是把找到的最小值放在參與排序元素里的第一位 >>>data_list = [5, 3, 7, 9, 6, 1, 10] >>>select_sort(data_list) >>>print(data_list) [1, 3, 5, 6, 7, 9, 10]
5、插入排序法
-
基本思想
輸入一個元素,檢查數組列表中的每個元素,將其插入到一個已經排好序的數列中的適當位置,使數列依然有序,當最后一個元素放入合適位置時,該數組排序完畢。
-
示例
假設用戶輸入序列為[7,3,1,4,8] (目標:判斷數字有沒有在正確的位置上,做法:與左邊的數字對比)
(1)首先從第二個數字3開始,看看3有沒在正確的位置上,與7對比,比7小,故交換位置,此時序列變為
[3,7,1,4,8]
(2)看看第三個數字1是否在正確的位置上,與7比較,比7小,故與7交換,再與左邊的3比較,比3小,再次與3交換,此時序列變為[1,3,7,4,8]
(3)看看第四個數字4是否在正確的位置上,與7比較,比7小,故與7交換,再與左邊的3比較,比3大,不用交換了,此時序列變為[1,3,4,7,8]
(3)看看第五個數字8是否在正確的位置上,與7比較,比7大,不用交換了,元素遍歷完畢,排序完成
-
Python代碼
def insert_sort(data_list):#插入排序法 length = len(data_list) for i in range(1,length): #從第二個元素開始尋找其“正確”位置 key = data_list[i] #設置當前元素值為key j = i - 1 #與當前值key的前一個元素對比 while j >= 0 and data_list[j] > key: data_list[j+1] = data_list[j] #前一個元素大于當前值key,則將前一個元素后移一位 j-=1 data_list[j+1] = key #j+1即為當前值key的合適位置 return data_list >>>data_list = [5, 3, 7, 9, 6, 1, 10] >>>print(insert_sort(data_list)) [1, 3, 5, 6, 7, 9, 10]
6、歸并排序法(合并排序法)
-
基本思想
典型的是二路合并排序,將原始數據集分成兩部分(不一定能夠均分),分別對它們進行排序,然后將排序后的子數據集進行合并,這是典型的分治法策略(分治思想是將每個問題分解成個個小問題,將每個小問題解決,然后合并)。時間復雜度O(nlogn)
-
步驟
(1):將n個元素分成兩個含n/2元素的子序列
(2):用歸并排序法將兩個子序列遞歸排序(最后可以將整個原序列分解成n個子序列)
(3):合并兩個已排序好的子序列(合并的過程是將兩個子序列排序,合成一個有序的序列)
-
示例
假設輸入序列為[54, 26, 93, 17, 77, 31, 44, 55, 20]
(1)首先將序列拆分成二等分:[54,26,93,17]和[77, 31, 44, 55, 20]
(2)對左邊的序列[54,26,93,17]繼續執行拆分,分為[54,26]和[93,17],再遞歸拆分,分為[54]和[26],此時序列中均只剩一個元素了,則進行合并比較操作,54>26,result=[26,54]
(3)再對右邊序列[93,17]拆分,分為[93]和[17],序列中均只剩一個元素了,故進行合并比較操作,93>17,result=[26,54]
(4)對(2)(3)步得到的序列[26,54]和[17,93]遞歸合并排序(在此講述一次合并步驟,其他不再贅述),首先i=0,j=0,26>17,故在result=[ ]中加入17,此時移動指針變為j,故j+=1;再繼續比較,26<93,在result中加入26,則result=[17,26],這時移動指針變為i了,故i+=1;再繼續比較54和93,54<93,在result中加入54,則result=[17,26,54];此時左邊序列已遍歷完,故可直接將右邊序列元素加到result后面了,即result += right[j:],得到result=[17,26,54,93]
(5){77, 31, 44, 55, 20}操作也同理,得到[20,31,44,55,77]
(6)最后對[17,26,54,93]與[20,31,44,55,77]進行合并排序即可
歸并排序?
歸并排序2 -
Python代碼
def merge_sort(data_lists): # 歸并排序 if len(data_lists) <= 1:#只有一個元素時,不進行切分,而是返回列表,緊接著開始第一次merge return data_lists num = int(len(data_lists) / 2)#每次都是二等分 left = merge_sort(data_lists[:num])#遞歸執行:拆分成二等份步驟 right = merge_sort(data_lists[num:]) return merge(left, right) def merge(left, right): i, j = 0, 0 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 #退出循環時,表left或者right有一個已經遍歷完畢。假設right遍歷完畢,則是將剩余的left里的值全部添加到result中,此時right[j:]為[]的 result += left[i:] result += right[j:] return result >>>a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] >>>print(merge_sort(a_list)) [17, 20, 26, 31, 44, 54, 55, 77, 93]
7、希爾排序法
-
基本思想
是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。其先取一個小于n的整數d1作為第一個增量,把文件的全部記錄分組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然后,取第二個增量d2<d1重復上述的分組和排序,直至所取的增量dt=1(dt<dt-1<...<d2<d1)即所有記錄放在同一組中進行直接插入排序為止。(實質是分組插入)
-
示例
假設用戶輸入[49,38,65,97,76,13,27,49,55,04],
(1)第一趟初始增量d1=10/2=5,即將10個待排記錄增量分為5個子序列,分別為[49,13],[38,27],[65,49],[97,55],[76,04]
(2)對上述5個分組分別進行插入排序(具體插入排序步驟看上面講述),每次排序時插回原序列對應的位置中,如[49,13],開始時49下標為0,13下標為5,而13<39,故排序后,下標0的位置是13,而49移動到原來13所在的下標位置5,其他同理,第一趟排序后序列變為[13,27,49,55,04,49,38,65,97,76]
(3)縮小增量d2=5/2=2,即將第一趟排序后的序列分為2組,分別為[13,49,04,38,97],[27,55,49,65,76]
(4)對上述2個分組分別進行插入排序(具體插入排序步驟看上面講述),第2趟排序后序列變為[04,27,13,49,38,55,49,65,97,76]
(5)縮小增量d3=2/2=1,即將第二趟排序后的序列分為1組,[04,27,13,49,38,55,49,65,97,76]
(6)對上述分組[04,27,13,49,38,55,49,65,97,76]進行插入排序,第3趟排序后序列變為[04,13,27,38,49,49,55,65,76,97]
希爾排序 -
Python代碼
def shell_sort(a_list):#希爾排序 sublist_count = len(a_list) // 2 #增量因子d while sublist_count > 0: for start_position in range(sublist_count): gap_insertion_sort(a_list, start_position, sublist_count) #按照d分組后得到的值進行插入排序 print("增量因子為:", sublist_count, " 排序后列表為:", a_list) sublist_count = sublist_count // 2 #增量因子d=1時,完成所有排序 def gap_insertion_sort(a_list, start, gap):#插入排序法的步驟 for i in range(start + gap, len(a_list), gap): #從第二個增量后的值start+gap開始進行插入排序 current_value = a_list[i] position = i while position >= gap and a_list[position - gap] > current_value: a_list[position] = a_list[position - gap] position = position - gap a_list[position] = current_value >>>a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] >>>shell_sort(a_list) print(a_list) 增量因子為: 4 排序后列表為: [20, 26, 44, 17, 54, 31, 93, 55, 77] 增量因子為: 2 排序后列表為: [20, 17, 44, 26, 54, 31, 77, 55, 93] 增量因子為: 1 排序后列表為: [17, 20, 26, 31, 44, 54, 55, 77, 93] [17, 20, 26, 31, 44, 54, 55, 77, 93]