㈠ 常見的幾種排序演算法總結
對於非科班生的我來說,演算法似乎對我來說是個難點,查閱了一些資料,趁此來了解一下幾種排序演算法。
首先了解一下,什麼是程序
關於排序演算法通常我們所說的往往指的是內部排序演算法,即數據記錄在內存中進行排序。
排序演算法大體可分為兩種:
一種是比較排序,時間復雜度O(nlogn) ~ O(n^2),主要有:冒泡排序,選擇排序,插入排序,歸並排序,堆排序,快速排序等。
另一種是非比較排序,時間復雜度可以達到O(n),主要有:計數排序,基數排序,桶排序等
冒泡排序它重復地走訪過要排序的元素,一次比較相鄰兩個元素,如果他們的順序錯誤就把他們調換過來,直到沒有元素再需要交換,排序完成。這個演算法的名字由來是因為越小(或越大)的元素會經由交換慢慢「浮」到數列的頂端。
選擇排序類似於冒泡排序,只不過選擇排序是首先在未排序的序列中找到最小值(最大值),放到序列的起始位置,然後再從剩餘未排序元素中繼續尋找最小(大)元素,放到已排序序列的末尾,以此類推,直到所有元素均排序完畢。
插入排序比冒泡排序和選擇排序更有效率,插入排序類似於生活中抓撲克牌來。
插入排序具體演算法描述,以數組[3, 2, 4, 5, 1]為例。
前面三種排序演算法只有教學價值,因為效率低,很少實際使用。歸並排序(Merge sort)則是一種被廣泛使用的排序方法。
它的基本思想是,將兩個已經排序的數組合並,要比從頭開始排序所有元素來得快。因此,可以將數組拆開,分成n個只有一個元素的數組,然後不斷地兩兩合並,直到全部排序完成。
以對數組[3, 2, 4, 5, 1] 進行從小到大排序為例,步驟如下:
有了merge函數,就可以對任意數組排序了。基本方法是將數組不斷地拆成兩半,直到每一半隻包含零個元素或一個元素為止,然後就用merge函數,將拆成兩半的數組不斷合並,直到合並成一整個排序完成的數組。
快速排序(quick sort)是公認最快的排序演算法之一,有著廣泛的應用。
快速排序演算法步驟
參考:
常用排序演算法總結(一)
阮一峰-演算法總結
㈡ 常用的數據排序演算法有哪些,各有什麼特點舉例結合一種排序演算法並應用數組進行數據排序。
排序簡介
排序是數據處理中經常使用的一種重要運算,在計算機及其應用系統中,花費在排序上的時間在系統運行時間中佔有很大比重;並且排序本身對推動演算法分析的發展也起很大作用。目前已有上百種排序方法,但尚未有一個最理想的盡如人意的方法,本章介紹常用的如下排序方法,並對它們進行分析和比較。
1、插入排序(直接插入排序、折半插入排序、希爾排序);
2、交換排序(起泡排序、快速排序);
3、選擇排序(直接選擇排序、堆排序);
4、歸並排序;
5、基數排序;
學習重點
1、掌握排序的基本概念和各種排序方法的特點,並能加以靈活應用;
2、掌握插入排序(直接插入排序、折半插入排序、希爾排序)、交換排序(起泡排序、快速排序)、選擇排序(直接選擇排序、堆排序)、二路歸並排序的方法及其性能分析方法;
3、了解基數排序方法及其性能分析方法。
排序(sort)或分類
所謂排序,就是要整理文件中的記錄,使之按關鍵字遞增(或遞減)次序排列起來。其確切定義如下:
輸入:n個記錄R1,R2,…,Rn,其相應的關鍵字分別為K1,K2,…,Kn。
輸出:Ril,Ri2,…,Rin,使得Ki1≤Ki2≤…≤Kin。(或Ki1≥Ki2≥…≥Kin)。
1.被排序對象--文件
被排序的對象--文件由一組記錄組成。
記錄則由若干個數據項(或域)組成。其中有一項可用來標識一個記錄,稱為關鍵字項。該數據項的值稱為關鍵字(Key)。
注意:
在不易產生混淆時,將關鍵字項簡稱為關鍵字。
2.排序運算的依據--關鍵字
用來作排序運算依據的關鍵字,可以是數字類型,也可以是字元類型。
關鍵字的選取應根據問題的要求而定。
【例】在高考成績統計中將每個考生作為一個記錄。每條記錄包含准考證號、姓名、各科的分數和總分數等項內容。若要惟一地標識一個考生的記錄,則必須用"准考證號"作為關鍵字。若要按照考生的總分數排名次,則需用"總分數"作為關鍵字。
排序的穩定性
當待排序記錄的關鍵字均不相同時,排序結果是惟一的,否則排序結果不唯一。
在待排序的文件中,若存在多個關鍵字相同的記錄,經過排序後這些具有相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具有相同關鍵字的記錄之間的相對次序發生變化,則稱這種排序方法是不穩定的。
注意:
排序演算法的穩定性是針對所有輸入實例而言的。即在所有可能的輸入實例中,只要有一個實例使得演算法不滿足穩定性要求,則該排序演算法就是不穩定的。
排序方法的分類
1.按是否涉及數據的內、外存交換分
在排序過程中,若整個文件都是放在內存中處理,排序時不涉及數據的內、外存交換,則稱之為內部排序(簡稱內排序);反之,若排序過程中要進行數據的內、外存交換,則稱之為外部排序。
注意:
① 內排序適用於記錄個數不很多的小文件
② 外排序則適用於記錄個數太多,不能一次將其全部記錄放人內存的大文件。
2.按策略劃分內部排序方法
可以分為五類:插入排序、選擇排序、交換排序、歸並排序和分配排序。
排序演算法分析
1.排序演算法的基本操作
大多數排序演算法都有兩個基本的操作:
(1) 比較兩個關鍵字的大小;
(2) 改變指向記錄的指針或移動記錄本身。
注意:
第(2)種基本操作的實現依賴於待排序記錄的存儲方式。
2.待排文件的常用存儲方式
(1) 以順序表(或直接用向量)作為存儲結構
排序過程:對記錄本身進行物理重排(即通過關鍵字之間的比較判定,將記錄移到合適的位置)
(2) 以鏈表作為存儲結構
排序過程:無須移動記錄,僅需修改指針。通常將這類排序稱為鏈表(或鏈式)排序;
(3) 用順序的方式存儲待排序的記錄,但同時建立一個輔助表(如包括關鍵字和指向記錄位置的指針組成的索引表)
排序過程:只需對輔助表的表目進行物理重排(即只移動輔助表的表目,而不移動記錄本身)。適用於難於在鏈表上實現,仍需避免排序過程中移動記錄的排序方法。
3.排序演算法性能評價
(1) 評價排序演算法好壞的標准
評價排序演算法好壞的標准主要有兩條:
① 執行時間和所需的輔助空間
② 演算法本身的復雜程度
(2) 排序演算法的空間復雜度
若排序演算法所需的輔助空間並不依賴於問題的規模n,即輔助空間是O(1),則稱之為就地排序(In-PlaceSou)。
非就地排序一般要求的輔助空間為O(n)。
(3) 排序演算法的時間開銷
大多數排序演算法的時間開銷主要是關鍵字之間的比較和記錄的移動。有的排序演算法其執行時間不僅依賴於問題的規模,還取決於輸入實例中數據的狀態。
文件的順序存儲結構表示
#define n l00 //假設的文件長度,即待排序的記錄數目
typedef int KeyType; //假設的關鍵字類型
typedef struct{ //記錄類型
KeyType key; //關鍵字項
InfoType otherinfo;//其它數據項,類型InfoType依賴於具體應用而定義
}RecType;
typedef RecType SeqList[n+1];//SeqList為順序表類型,表中第0個單元一般用作哨兵
注意:
若關鍵字類型沒有比較算符,則可事先定義宏或函數來表示比較運算。
【例】關鍵字為字元串時,可定義宏"#define LT(a,b)(Stromp((a),(b))<0)"。那麼演算法中"a<b"可用"LT(a,b)"取代。若使用C++,則定義重載的算符"<"更為方便。
按平均時間將排序分為四類:
(1)平方階(O(n2))排序
一般稱為簡單排序,例如直接插入、直接選擇和冒泡排序;
(2)線性對數階(O(nlgn))排序
如快速、堆和歸並排序;
(3)O(n1+£)階排序
£是介於0和1之間的常數,即0<£<1,如希爾排序;
(4)線性階(O(n))排序
如桶、箱和基數排序。
各種排序方法比較
簡單排序中直接插入最好,快速排序最快,當文件為正序時,直接插入和冒泡均最佳。
影響排序效果的因素
因為不同的排序方法適應不同的應用環境和要求,所以選擇合適的排序方法應綜合考慮下列因素:
①待排序的記錄數目n;
②記錄的大小(規模);
③關鍵字的結構及其初始狀態;
④對穩定性的要求;
⑤語言工具的條件;
⑥存儲結構;
⑦時間和輔助空間復雜度等。
不同條件下,排序方法的選擇
(1)若n較小(如n≤50),可採用直接插入或直接選擇排序。
當記錄規模較小時,直接插入排序較好;否則因為直接選擇移動的記錄數少於直接插人,應選直接選擇排序為宜。
(2)若文件初始狀態基本有序(指正序),則應選用直接插人、冒泡或隨機的快速排序為宜;
(3)若n較大,則應採用時間復雜度為O(nlgn)的排序方法:快速排序、堆排序或歸並排序。
快速排序是目前基於比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分布時,快速排序的平均時間最短;
堆排序所需的輔助空間少於快速排序,並且不會出現快速排序可能出現的最壞情況。這兩種排序都是不穩定的。
若要求排序穩定,則可選用歸並排序。但本章介紹的從單個記錄起進行兩兩歸並的 排序演算法並不值得提倡,通常可以將它和直接插入排序結合在一起使用。先利用直接插入排序求得較長的有序子文件,然後再兩兩歸並之。因為直接插入排序是穩定的,所以改進後的歸並排序仍是穩定的。
4)在基於比較的排序方法中,每次比較兩個關鍵字的大小之後,僅僅出現兩種可能的轉移,因此可以用一棵二叉樹來描述比較判定過程。
當文件的n個關鍵字隨機分布時,任何藉助於"比較"的排序演算法,至少需要O(nlgn)的時間。
箱排序和基數排序只需一步就會引起m種可能的轉移,即把一個記錄裝入m個箱子之一,因此在一般情況下,箱排序和基數排序可能在O(n)時間內完成對n個記錄的排序。但是,箱排序和基數排序只適用於像字元串和整數這類有明顯結構特徵的關鍵字,而當關鍵字的取值范圍屬於某個無窮集合(例如實數型關鍵字)時,無法使用箱排序和基數排序,這時只有藉助於"比較"的方法來排序。
若n很大,記錄的關鍵字位數較少且可以分解時,採用基數排序較好。雖然桶排序對關鍵字的結構無要求,但它也只有在關鍵字是隨機分布時才能使平均時間達到線性階,否則為平方階。同時要注意,箱、桶、基數這三種分配排序均假定了關鍵字若為數字時,則其值均是非負的,否則將其映射到箱(桶)號時,又要增加相應的時間。
(5)有的語言(如Fortran,Cobol或Basic等)沒有提供指針及遞歸,導致實現歸並、快速(它們用遞歸實現較簡單)和基數(使用了指針)等排序演算法變得復雜。此時可考慮用其它排序。
(6)本章給出的排序演算法,輸人數據均是存儲在一個向量中。當記錄的規模較大時,為避免耗費大量的時間去移動記錄,可以用鏈表作為存儲結構。譬如插入排序、歸並排序、基數排序都易於在鏈表上實現,使之減少記錄的移動次數。但有的排序方法,如快速排序和堆排序,在鏈表上卻難於實現,在這種情況下,可以提取關鍵字建立索引表,然後對索引表進行排序。然而更為簡單的方法是:引人一個整型向量t作為輔助表,排序前令t[i]=i(0≤i<n),若排序演算法中要求交換R[i]和R[j],則只需交換t[i]和t[j]即可;排序結束後,向量t就指示了記錄之間的順序關系:
R[t[0]].key≤R[t[1]].key≤…≤R[t[n-1]].key
若要求最終結果是:
R[0].key≤R[1].key≤…≤R[n-1].key
則可以在排序結束後,再按輔助表所規定的次序重排各記錄,完成這種重排的時間是O(n)。
㈢ 排序演算法在實際應用什麼時候可以用上
很多地方,比如excel中就有排序,肯定是按照某種演算法排序的。平常的應用對排序演算法要求不高,但在排序大量數據時排序演算法的時間復雜度和空間復雜度將有很大影響,比如圖像處理中一種非線性平滑圖像的方法-中值濾波中就用到了排序演算法。
㈣ 常用的排序演算法有哪些
排序另一種分法
外排序:需要在內外存之間多次交換數據才能進行
內排序:
插入類排序
直接插入排序
希爾排序
選擇類排序
簡單選擇排序
堆排序
交換類排序
冒泡排序
快速排序
歸並類排序
歸並排序
㈤ 幾種常見簡單排序演算法
排序演算法一般分為以下幾種:
(1)非線性時間比較類排序:交換類排序(快速排序和冒泡排序)、插入類排序(簡單插入排序和希爾排序)、選擇類排序(簡單選擇排序和堆排序)、歸並排序(二路歸並排序和多路歸並排序);
(2)線性時間非比較類排序:計數排序、基數排序和桶排序。
㈥ 排序演算法通常使用什麼數據結構和存儲結構為什麼
排序演算法需要按照其中的某個或某些關鍵字的大小,遞增或遞減的排列起來的操作;首先要求其具有一定的穩定性,即當兩個相同的元素同時出現於某個序列之中,則經過一定的排序演算法之後,兩者在排序前後的相對位置不發生變化。
換言之,即便是兩個完全相同的元素,它們在排序過程中也是各有區別的,不允許混淆不清。
(6)排序演算法適用擴展閱讀:
快速排序的基本思想:通過一趟排序演算法把所需要排序的序列的元素分割成兩大塊,其中,一部分的元素都要小於或等於另外一部分的序列元素。
然後仍根據該種方法對劃分後的這兩塊序列的元素分別再次實行快速排序演算法,排序實現的整個過程可以是遞歸的來進行調用,最終能夠實現將所需排序的無序序列元素變為一個有序的序列。
㈦ 幾種排序演算法的比較
一、八大排序演算法的總體比較
4.3、堆的插入:
每次插入都是將新數據放在數組最後。可以發現從這個新數據的父結點到根結點必然為一個有序的數列,然後將這個新數據插入到這個有序數據中
(1)用大根堆排序的基本思想
先將初始數組建成一個大根堆,此對為初始的無序區;
再將最大的元素和無序區的最後一個記錄交換,由此得到新的無序區和有序區,且滿足<=的值;
由於交換後新的根可能違反堆性質,故將當前無序區調整為堆。然後再次將其中最大的元素和該區間的最後一個記錄交換,由此得到新的無序區和有序區,且仍滿足關系的值<=的值,同樣要將其調整為堆;
..........
直到無序區只有一個元素為止;
4.4:應用
尋找M個數中的前K個最小的數並保持有序;
時間復雜度:O(K)[創建K個元素最大堆的時間復雜度] +(M-K)*log(K)[對剩餘M-K個數據進行比較並每次對最大堆進行從新最大堆化]
5.希爾排序
(1)基本思想
先將整個待排序元素序列分割成若乾子序列(由相隔某個「增量」的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序(因為直接插入排序在元素基本有序的情況下,效率很高);
(2)適用場景
比較在希爾排序中是最主要的操作,而不是交換。用已知最好的步長序列的希爾排序比直接插入排序要快,甚至在小數組中比快速排序和堆排序還快,但在涉及大量數據時希爾排序還是不如快排;
6.歸並排序
(1)基本思想
首先將初始序列的n個記錄看成是n個有序的子序列,每個子序列的長度為1,然後兩兩歸並,得到n/2個長度為2的有序子序列,在此基礎上,再對長度為2的有序子序列進行兩兩歸並,得到若干個長度為4的有序子序列,以此類推,直到得到一個長度為n的有序序列為止;
(2)適用場景
若n較大,並且要求排序穩定,則可以選擇歸並排序;
7.簡單選擇排序
(1)基本思想
第一趟:從第一個記錄開始,將後面n-1個記錄進行比較,找到其中最小的記錄和第一個記錄進行交換;
第二趟:從第二個記錄開始,將後面n-2個記錄進行比較,找到其中最小的記錄和第2個記錄進行交換;
...........
第i趟:從第i個記錄開始,將後面n-i個記錄進行比較,找到其中最小的記錄和第i個記錄進行交換;
以此類推,經過n-1趟比較,將n-1個記錄排到位,剩下一個最大記錄直接排在最後;
㈧ 常見查找和排序演算法
查找成功最多要n 次,平均(n+1)/2次, 時間復雜度為O(n) 。
優點:既適用順序表也適用單鏈表,同時對表中元素順序無要求,給插入帶來方便,只需插入表尾即可。
缺點:速度較慢。
改進:在表尾設置一個崗哨,這樣不用去循環判斷數組下標是否越界,因為最後必然成立。
適用條件:
二分查找的判定樹不僅是二叉排序樹,而且是一棵理想平衡樹。 時間復雜度為O(lbn) 。
循環實現
遞歸實現
待排序的元素需要實現 Java 的 Comparable 介面,該介面有 compareTo() 方法,可以用它來判斷兩個元素的大小關系。
從數組中選擇最小元素,將它與數組的第一個元素交換位置。再從數組剩下的元素中選擇出最小的元素,將它與數組的第二個元素交換位置。不斷進行這樣的操作,直到將整個數組排序。
選擇排序需要 ~N2/2 次比較和 ~N 次交換,==它的運行時間與輸入無關==,這個特點使得它對一個已經排序的數組也需要這么多的比較和交換操作。
從左到右不斷 交換相鄰逆序的元素 ,在一輪的循環之後,可以讓未排序的最大元素上浮到右側。
在一輪循環中,如果沒有發生交換,那麼說明數組已經是有序的,此時可以直接退出。
每次都 將當前元素插入到左側已經排序的數組中 ,使得插入之後左側數組依然有序。
對於數組 {3, 5, 2, 4, 1},它具有以下逆序:(3, 2), (3, 1), (5, 2), (5, 4), (5, 1), (2, 1), (4, 1),插入排序每次只能交換相鄰元素,令逆序數量減少 1,因此插入排序需要交換的次數為逆序數量。
==插入排序的時間復雜度取決於數組的初始順序,如果數組已經部分有序了,那麼逆序較少,需要的交換次數也就較少,時間復雜度較低==。
對於大規模的數組,插入排序很慢,因為它只能交換相鄰的元素,每次只能將逆序數量減少 1。希爾排序的出現就是為了解決插入排序的這種局限性,它通過交換不相鄰的元素,每次可以將逆序數量減少大於 1。
希爾排序使用插入排序對間隔 h 的序列進行排序。通過不斷減小 h,最後令 h=1,就可以使得整個數組是有序的。
希爾排序的運行時間達不到平方級別,使用遞增序列 1, 4, 13, 40, ... 的希爾排序所需要的比較次數不會超過 N 的若干倍乘於遞增序列的長度。後面介紹的高級排序演算法只會比希爾排序快兩倍左右。
歸並排序的思想是將數組分成兩部分,分別進行排序,然後歸並起來。
歸並方法將數組中兩個已經排序的部分歸並成一個。
將一個大數組分成兩個小數組去求解。
因為每次都將問題對半分成兩個子問題,這種對半分的演算法復雜度一般為 O(NlogN)。
先歸並那些微型數組,然後成對歸並得到的微型數組。
取 a[l] 作為切分元素,然後從數組的左端向右掃描直到找到第一個大於等於它的元素,再從數組的右端向左掃描找到第一個小於它的元素,交換這兩個元素。不斷進行這個過程,就可以保證左指針 i 的左側元素都不大於切分元素,右指針 j 的右側元素都不小於切分元素。當兩個指針相遇時,將切分元素 a[l] 和 a[j] 交換位置。
快速排序是原地排序,不需要輔助數組,但是遞歸調用需要輔助棧。
快速排序最好的情況下是每次都正好將數組對半分,這樣遞歸調用次數才是最少的。這種情況下比較次數為 CN=2CN/2+N,復雜度為 O(NlogN)。
最壞的情況下,第一次從最小的元素切分,第二次從第二小的元素切分,如此這般。因此最壞的情況下需要比較 N2/2。為了防止數組最開始就是有序的,在進行快速排序時需要隨機打亂數組。
因為快速排序在小數組中也會遞歸調用自己,對於小數組,插入排序比快速排序的性能更好,因此在小數組中可以切換到插入排序。
最好的情況下是每次都能取數組的中位數作為切分元素,但是計算中位數的代價很高。一種折中方法是取 3 個元素,並將大小居中的元素作為切分元素。
對於有大量重復元素的數組,可以將數組切分為三部分,分別對應小於、等於和大於切分元素。
三向切分快速排序對於有大量重復元素的隨機數組可以在線性時間內完成排序。
快速排序的 partition() 方法,會返回一個整數 j 使得 a[l..j-1] 小於等於 a[j],且 a[j+1..h] 大於等於 a[j],此時 a[j] 就是數組的第 j 大元素。
可以利用這個特性找出數組的第 k 大的元素。
該演算法是線性級別的,假設每次能將數組二分,那麼比較的總次數為 (N+N/2+N/4+..),直到找到第 k 個元素,這個和顯然小於 2N。
堆中某個節點的值總是大於等於其子節點的值,並且堆是一顆完全二叉樹。
堆可以用數組來表示,這是因為堆是完全二叉樹,而完全二叉樹很容易就存儲在數組中。位置 k 的節點的父節點位置為 k/2,而它的兩個子節點的位置分別為 2k 和 2k+1。這里不使用數組索引為 0 的位置,是為了更清晰地描述節點的位置關系。
在堆中,當一個節點比父節點大,那麼需要交換這個兩個節點。交換後還可能比它新的父節點大,因此需要不斷地進行比較和交換操作,把這種操作稱為上浮。
類似地,當一個節點比子節點來得小,也需要不斷地向下進行比較和交換操作,把這種操作稱為下沉。一個節點如果有兩個子節點,應當與兩個子節點中最大那個節點進行交換。
將新元素放到數組末尾,然後上浮到合適的位置。
從數組頂端刪除最大的元素,並將數組的最後一個元素放到頂端,並讓這個元素下沉到合適的位置。
把最大元素和當前堆中數組的最後一個元素交換位置,並且不刪除它,那麼就可以得到一個從尾到頭的遞減序列,從正向來看就是一個遞增序列,這就是堆排序。
一個堆的高度為logN,因此在堆中插入元素和刪除最大元素的復雜度都為 logN。
對於堆排序,由於要對 N 個節點進行下沉操作,因此復雜度為 NlogN。
堆排序是一種原地排序,沒有利用額外的空間。
現代操作系統很少使用堆排序,因為它無法利用局部性原理進行緩存,也就是數組元素很少和相鄰的元素進行比較和交換。
計數排序的核心在於將輸入的數據值轉化為鍵存儲在額外開辟的數組空間中。作為一種線性時間復雜度的排序,==計數排序要求輸入的數據必須是有確定范圍的整數==。
當輸入的元素是 n 個 0 到 k 之間的整數時,它的==運行時間是 O(n + k)==。計數排序不是比較排序,排序的速度快於任何比較排序演算法。由於用來計數的數組C的長度取決於待排序數組中數據的范圍(等於待排序數組的最大值與最小值的差加上1),這使得計數排序對於數據范圍很大的數組,需要大量時間和內存。比較適合用來排序==小范圍非負整數數組的數組==。
桶排序是計數排序的升級版。它利用了函數的映射關系,高效與否的關鍵就在於這個映射函數的確定。為了使桶排序更加高效,我們需要做到這兩點:
同時,對於桶中元素的排序,選擇何種比較排序演算法對於性能的影響至關重要。
當輸入數據均勻分配到每一個桶時最快,當都分配到同一個桶時最慢。
實間復雜度N*K
快速排序是最快的通用排序演算法,它的內循環的指令很少,而且它還能利用緩存,因為它總是順序地訪問數據。它的運行時間近似為 ~cNlogN,這里的 c 比其它線性對數級別的排序演算法都要小。
使用三向切分快速排序,實際應用中可能出現的某些分布的輸入能夠達到線性級別,而其它排序演算法仍然需要線性對數時間。
㈨ 數據結構排序演算法有哪些常用的
最常用的是快速排序,基數排序,計數排序,歸並排序,堆排序,(偶爾還有插入排序)
都有各自的應用,快排就是單純的快,但是特殊數據下復雜度會退化
基數排序可以配合一些特定的演算法,譬如後綴數組的構建
計數排序簡單且常用,通常排序值域小但是數據量大的情況
歸並直接用來排序並不多,但是可以用來求解一些其他問題,本身的思想也非常重要,有很多拓展的演算法(不是排序演算法)
堆排序勝在穩定,不論數據如何最壞都是O(nlogn),一般情況比快速排序慢些,但是極端情況下表現十分優秀,常用來配合快速排序,優化其穩定性
插入排序適合極少量數據的排序(幾個到十幾個),速度要比這些高級演算法快一些
㈩ 常用的排序演算法都有哪些
排序演算法 所謂排序,就是使一串記錄,按照其中的某個或某些關鍵字的大小,遞增或遞減的排列起來的操作。
分類
在計算機科學所使用的排序演算法通常被分類為:
計算的復雜度(最差、平均、和最好表現),依據串列(list)的大小(n)。一般而言,好的表現是O。(n log n),且壞的行為是Ω(n2)。對於一個排序理想的表現是O(n)。僅使用一個抽象關鍵比較運算的排序演算法總平均上總是至少需要Ω(n log n)。
記憶體使用量(以及其他電腦資源的使用)
穩定度:穩定排序演算法會依照相等的關鍵(換言之就是值)維持紀錄的相對次序。也就是一個排序演算法是穩定的,就是當有兩個有相等關鍵的紀錄R和S,且在原本的串列中R出現在S之前,在排序過的串列中R也將會是在S之前。
一般的方法:插入、交換、選擇、合並等等。交換排序包含冒泡排序(bubble sort)和快速排序(quicksort)。選擇排序包含shaker排序和堆排序(heapsort)。
當相等的元素是無法分辨的,比如像是整數,穩定度並不是一個問題。然而,假設以下的數對將要以他們的第一個數字來排序。
(4, 1) (3, 1) (3, 7) (5, 6)
在這個狀況下,有可能產生兩種不同的結果,一個是依照相等的鍵值維持相對的次序,而另外一個則沒有:
(3, 1) (3, 7) (4, 1) (5, 6) (維持次序)
(3, 7) (3, 1) (4, 1) (5, 6) (次序被改變)
不穩定排序演算法可能會在相等的鍵值中改變紀錄的相對次序,但是穩定排序演算法從來不會如此。不穩定排序演算法可以被特別地時作為穩定。作這件事情的一個方式是人工擴充鍵值的比較,如此在其他方面相同鍵值的兩個物件間之比較,就會被決定使用在原先資料次序中的條目,當作一個同分決賽。然而,要記住這種次序通常牽涉到額外的空間負擔。
排列演算法列表
在這個表格中,n是要被排序的紀錄數量以及k是不同鍵值的數量。
穩定的
冒泡排序(bubble sort) — O(n2)
雞尾酒排序 (Cocktail sort, 雙向的冒泡排序) — O(n2)
插入排序 (insertion sort)— O(n2)
桶排序 (bucket sort)— O(n); 需要 O(k) 額外 記憶體
計數排序 (counting sort) — O(n+k); 需要 O(n+k) 額外 記憶體
歸並排序 (merge sort)— O(n log n); 需要 O(n) 額外記憶體
原地歸並排序 — O(n2)
二叉樹排序 (Binary tree sort) — O(n log n); 需要 O(n) 額外記憶體
鴿巢排序 (Pigeonhole sort) — O(n+k); 需要 O(k) 額外記憶體
基數排序 (radix sort)— O(n·k); 需要 O(n) 額外記憶體
Gnome sort — O(n2)
Library sort — O(n log n) with high probability, 需要 (1+ε)n 額外記憶體
不穩定
選擇排序 (selection sort)— O(n2)
希爾排序 (shell sort)— O(n log n) 如果使用最佳的現在版本
Comb sort — O(n log n)
堆排序 (heapsort)— O(n log n)
Smoothsort — O(n log n)
快速排序 (quicksort)— O(n log n) 期望時間, O(n2) 最壞情況; 對於大的、亂數串列一般相信是最快的已知排序
Introsort — O(n log n)
Patience sorting — O(n log n + k) 最外情況時間, 需要 額外的 O(n + k) 空間, 也需要找到最長的遞增子序列(longest increasing subsequence)
不實用的排序演算法
Bogo排序 — O(n × n!) 期望時間, 無窮的最壞情況。
Stupid sort — O(n3); 遞回版本需要 O(n2) 額外記憶體
Bead sort — O(n) or O(√n), 但需要特別的硬體
Pancake sorting — O(n), 但需要特別的硬體
排序的演算法
排序的演算法有很多,對空間的要求及其時間效率也不盡相同。下面列出了一些常見的排序演算法。這裡面插入排序和冒泡排序又被稱作簡單排序,他們對空間的要求不高,但是時間效率卻不穩定;而後面三種排序相對於簡單排序對空間的要求稍高一點,但時間效率卻能穩定在很高的水平。基數排序是針對關鍵字在一個較小范圍內的排序演算法。
插入排序
冒泡排序
選擇排序
快速排序
堆排序
歸並排序
基數排序
希爾排序
插入排序
插入排序是這樣實現的:
首先新建一個空列表,用於保存已排序的有序數列(我們稱之為"有序列表")。
從原數列中取出一個數,將其插入"有序列表"中,使其仍舊保持有序狀態。
重復2號步驟,直至原數列為空。
插入排序的平均時間復雜度為平方級的,效率不高,但是容易實現。它藉助了"逐步擴大成果"的思想,使有序列表的長度逐漸增加,直至其長度等於原列表的長度。
冒泡排序
冒泡排序是這樣實現的:
首先將所有待排序的數字放入工作列表中。
從列表的第一個數字到倒數第二個數字,逐個檢查:若某一位上的數字大於他的下一位,則將它與它的下一位交換。
重復2號步驟,直至再也不能交換。
冒泡排序的平均時間復雜度與插入排序相同,也是平方級的,但也是非常容易實現的演算法。
選擇排序
選擇排序是這樣實現的:
設數組內存放了n個待排數字,數組下標從1開始,到n結束。
i=1
從數組的第i個元素開始到第n個元素,尋找最小的元素。
將上一步找到的最小元素和第i位元素交換。
如果i=n-1演算法結束,否則回到第3步
選擇排序的平均時間復雜度也是O(n²)的。
快速排序
現在開始,我們要接觸高效排序演算法了。實踐證明,快速排序是所有排序演算法中最高效的一種。它採用了分治的思想:先保證列表的前半部分都小於後半部分,然後分別對前半部分和後半部分排序,這樣整個列表就有序了。這是一種先進的思想,也是它高效的原因。因為在排序演算法中,演算法的高效與否與列表中數字間的比較次數有直接的關系,而"保證列表的前半部分都小於後半部分"就使得前半部分的任何一個數從此以後都不再跟後半部分的數進行比較了,大大減少了數字間不必要的比較。但查找數據得另當別論了。
堆排序
堆排序與前面的演算法都不同,它是這樣的:
首先新建一個空列表,作用與插入排序中的"有序列表"相同。
找到數列中最大的數字,將其加在"有序列表"的末尾,並將其從原數列中刪除。
重復2號步驟,直至原數列為空。
堆排序的平均時間復雜度為nlogn,效率高(因為有堆這種數據結構以及它奇妙的特徵,使得"找到數列中最大的數字"這樣的操作只需要O(1)的時間復雜度,維護需要logn的時間復雜度),但是實現相對復雜(可以說是這里7種演算法中比較難實現的)。
看起來似乎堆排序與插入排序有些相像,但他們其實是本質不同的演算法。至少,他們的時間復雜度差了一個數量級,一個是平方級的,一個是對數級的。
平均時間復雜度
插入排序 O(n2)
冒泡排序 O(n2)
選擇排序 O(n2)
快速排序 O(n log n)
堆排序 O(n log n)
歸並排序 O(n log n)
基數排序 O(n)
希爾排序 O(n1.25)
冒泡排序
654
比如說這個,我想讓它從小到大排序,怎麼做呢?
第一步:6跟5比,發現比它大,則交換。564
第二步:5跟4比,發現比它大,則交換。465
第三步:6跟5比,發現比它大,則交換。456