簡單排序
- 冒泡排序:循環(huán)遍歷左右比較,較小者左移或較大者后移;
- 選擇排序:在未排序序列中找到最小者元素一次放到已排序序列末尾;
- 插入排序:從未排序序列開始取出下一個元素依次從已排序序列從后向前掃描,插入到合適的位置
高效排序
- 快速排序:找到基準(zhǔn),進(jìn)行分區(qū)操作(基準(zhǔn)左邊的小,右邊大),遞歸對分區(qū)的分區(qū)繼續(xù)進(jìn)行分區(qū)操作,直至分區(qū)元素有序或則剩余一個元素;
- 堆排序:建堆、首尾交換、斷尾、建隊,直至剩余一個元素;
- 希爾排序:按照一定步長串起來各步長對應(yīng)的數(shù)字,對各個分區(qū)數(shù)字進(jìn)行插入排序,重復(fù)上述步驟,最后異步按照步長為1進(jìn)行插入排序;
基于分治遞歸
- 歸并排序:兩兩分組排序,排序后兩組合并再排序;
- 計數(shù)排序:用待排序的數(shù)作為計數(shù)數(shù)組的下標(biāo),統(tǒng)計每個數(shù)字的個數(shù)。然后依次輸出即可得到有序序列;
- 桶排序:把每個數(shù)字按照一定的映射函數(shù)放到相應(yīng)的桶中,然后對桶內(nèi)的數(shù)字排序;
- 基數(shù)排序:所有待比較數(shù)值(正整數(shù))統(tǒng)一為同樣的數(shù)位長度,數(shù)位較短的數(shù)前面補零。然后,從最低位開始,依次進(jìn)行一次排序。這樣從最低位排序一直到最高位排序完成以后,數(shù)列就變成一個有序序列
參考資料:十大排序算法
1.冒泡排序
通過與相鄰元素的比較和交換來把小的數(shù)交換到最前面
i∈[0,N-1) //循環(huán)N-1遍
j∈[0,N-1-i) //每遍循環(huán)要處理的無序部分
swap(j,j+1) //兩兩排序(升序/降序)
2.選擇排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續(xù)尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
3.插入排序
它的工作原理是通過構(gòu)建有序序列,對于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。插入排序在實現(xiàn)上,通常采用 in-place 排序(即只需用到 O(1) 的額外空間的排序),因而在從后向前掃描過程中,需要反復(fù)把已排序元素逐步向后挪位,為最新元素提供插入空間
一般來說,插入排序都采用 in-place 在數(shù)組上實現(xiàn)。具體算法描述如下:
- 從第一個元素開始,該元素可以認(rèn)為已經(jīng)被排序
- 取出下一個元素,在已經(jīng)排序的元素序列中從后向前掃描
- 如果該元素(已排序)大于新元素,將該元素移到下一位置
- 重復(fù)步驟 3,直到找到已排序的元素小于或者等于新元素的位置
- 將新元素插入到該位置后
- 重復(fù)步驟 2~5
4.快速排序
快速排序使用分治法(Divide and conquer)策略來把一個序列(list)分為兩個子序列(sub-lists)。
步驟為:
- 從數(shù)列中挑出一個元素,稱為 "基準(zhǔn)"(pivot),
- 重新排序數(shù)列,所有比基準(zhǔn)值小的元素擺放在基準(zhǔn)前面,所有比基準(zhǔn)值大的元素擺在基準(zhǔn)后面(相同的數(shù)可以到任一邊)。在這個分區(qū)結(jié)束之后,該基準(zhǔn)就處于數(shù)列的中間位置。這個稱為分區(qū)(partition)操作。
- 遞歸地(recursively)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。
遞歸到最底部時,數(shù)列的大小是零或一,也就是已經(jīng)排序好了。這個算法一定會結(jié)束,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最后的位置去。
5.堆排序
堆排序是借助堆來實現(xiàn)的選擇排序,思想同簡單的選擇排序,以下以大頂堆為例。注意:如果想升序排序就使用大頂堆,反之使用小頂堆。原因是堆頂元素需要交換到序列尾部。
首先,實現(xiàn)堆排序需要解決兩個問題:
如何由一個無序序列鍵成一個堆?
如何在輸出堆頂元素之后,調(diào)整剩余元素成為一個新的堆?
第一個問題,可以直接使用線性數(shù)組來表示一個堆,由初始的無序序列建成一個堆就需要自底向上從第一個非葉元素開始挨個調(diào)整成一個堆。
第二個問題,怎么調(diào)整成堆?首先是將堆頂元素和最后一個元素交換。然后比較當(dāng)前堆頂元素的左右孩子節(jié)點,因為除了當(dāng)前的堆頂元素,左右孩子堆均滿足條件,這時需要選擇當(dāng)前堆頂元素與左右孩子節(jié)點的較大者(大頂堆)交換,直至葉子節(jié)點。我們稱這個自堆頂自葉子的調(diào)整成為篩選。
從一個無序序列建堆的過程就是一個反復(fù)篩選的過程。若將此序列看成是一個完全二叉樹,則最后一個非終端節(jié)點是 n/2 取底個元素,由此篩選即可。
6.希爾排序
希爾排序,也稱遞減增量排序算法,是[插入排序]的一種更高效的改進(jìn)版本。希爾排序是非穩(wěn)定排序算法。
希爾排序是基于插入排序的以下兩點性質(zhì)而提出改進(jìn)方法的:
插入排序在對幾乎已經(jīng)排好序的數(shù)據(jù)操作時,效率高,即可以達(dá)到線性排序的效率
但插入排序一般來說是低效的,因為插入排序每次只能將數(shù)據(jù)移動一位
7.歸并排序
歸并排序是另一種不同的排序方法,因為歸并排序使用了遞歸分治的思想,所以理解起來比較容易。其基本思想是,先遞歸劃分子問題,然后合并結(jié)果。把待排序列看成由兩個有序的子序列,然后合并兩個子序列,然后把子序列看成由兩個有序序列。。。。。倒著來看,其實就是先兩兩合并,然后四四合并。。。最終形成有序序列。空間復(fù)雜度為 O(n),時間復(fù)雜度為 O(nlogn)。
8.計數(shù)排序
基本思想是,用待排序的數(shù)作為計數(shù)數(shù)組的下標(biāo),統(tǒng)計每個數(shù)字的個數(shù)。然后依次輸出即可得到有序序列。
9.桶排序
桶排序(Bucket sort)或所謂的箱排序,是一個排序算法,工作的原理是將數(shù)組分到有限數(shù)量的桶里。每個桶再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續(xù)使用桶排序進(jìn)行排序)。桶排序是鴿巢排序的一種歸納結(jié)果。當(dāng)要被排序的數(shù)組內(nèi)的數(shù)值是均勻分配的時候,桶排序使用線性時間(Θ(n))。但桶排序并不是比較排序,他不受到 O(n log n) 下限的影響。
桶排序以下列程序進(jìn)行:
- 設(shè)置一個定量的數(shù)組當(dāng)作空桶子。
- 尋訪序列,并且把項目一個一個放到對應(yīng)的桶子去。
- 對每個不是空的桶子進(jìn)行排序。
- 從不是空的桶子里把項目再放回原來的序列中。
10.基數(shù)排序
將所有待比較數(shù)值(正整數(shù))統(tǒng)一為同樣的數(shù)位長度,數(shù)位較短的數(shù)前面補零。然后,從最低位開始,依次進(jìn)行一次排序。這樣從最低位排序一直到最高位排序完成以后,數(shù)列就變成一個有序序列。
在前面的介紹和分析中我們提到了
- 冒泡排序、選擇排序、插入排序三種簡單的排序
- 及其變種快速排序、堆排序、希爾排序三種比較高效的排序。
- 后面我們又分析了基于分治遞歸思想的歸并排序還有計數(shù)排序、桶排序、基數(shù)排序三種線性排序。
我們可以知道排序算法要么簡單有效,要么是利用簡單排序的特點加以改進(jìn),要么是以空間換取時間在特定情況下的高效排序。但是這些排序方法都不是固定不變的,需要結(jié)合具體的需求和場景來選擇甚至組合使用。才能達(dá)到高效穩(wěn)定的目的。沒有最好的排序,只有最適合的排序。
下面就總結(jié)一下排序算法的各自的使用場景和適用場合。
從平均時間來看,快速排序是效率最高的,但快速排序在最壞情況下的時間性能不如堆排序和歸并排序。而后者相比較的結(jié)果是,在 n 較大時歸并排序使用時間較少,但使用輔助空間較多。
上面說的簡單排序包括除希爾排序之外的所有冒泡排序、插入排序、簡單選擇排序。其中直接插入排序最簡單,但序列基本有序或者 n 較小時,直接插入排序是好的方法,因此常將它和其他的排序方法,如快速排序、歸并排序等結(jié)合在一起使用。
基數(shù)排序的時間復(fù)雜度也可以寫成 O(d*n)。因此它最使用于 n 值很大而關(guān)鍵字較小的的序列。若關(guān)鍵字也很大,而序列中大多數(shù)記錄的最高關(guān)鍵字均不同,則亦可先按最高關(guān)鍵字不同,將序列分成若干小的子序列,而后進(jìn)行直接插入排序。
從方法的穩(wěn)定性來比較,基數(shù)排序是穩(wěn)定的內(nèi)排方法,所有時間復(fù)雜度為 O(n^2) 的簡單排序也是穩(wěn)定的。但是快速排序、堆排序、希爾排序等時間性能較好的排序方法都是不穩(wěn)定的。穩(wěn)定性需要根據(jù)具體需求選擇。
上面的算法實現(xiàn)大多數(shù)是使用線性存儲結(jié)構(gòu),像插入排序這種算法用鏈表實現(xiàn)更好,省去了移動元素的時間。具體的存儲結(jié)構(gòu)在具體的實現(xiàn)版本中也是不同的。