選擇排序厕怜、快速排序绑莺、希爾排序、堆排序不是穩(wěn)定的排序算法览妖,
冒泡排序簿盅、插入排序挥下、歸并排序和基數(shù)排序是穩(wěn)定的排序算法。
- 冒泡法:復(fù)雜度為O(n*n)桨醋。當(dāng)數(shù)據(jù)為正序棚瘟,將不會有交換。復(fù)雜度為O(0)喜最。
- 直接插入排序:O(n*n)
- 選擇排序:O(n*n)
- 快速排序:平均時間復(fù)雜度log2(n)*n
- 歸并排序:log2(n)*n
- 堆排序:log2(n)*n
- 希爾排序:算法的復(fù)雜度為n的1.2次冪
(1)冒泡排序
冒泡排序就是把小的元素往前調(diào)或者把大的元素往后調(diào)偎蘸。比較是相鄰的兩個元素比較,交換也發(fā)生在這兩個元素之間。所以迷雪,如果兩個元素相等限书,我想你是不會再無聊地把他們倆交換一下的;如果兩個相等的元素沒有相鄰章咧,那么即使通過前面的兩兩交換把兩個相鄰起來倦西,這時候也不會交換,所以相同元素的前后順序并沒有改變赁严,所以冒泡排序是一種穩(wěn)定排序算法扰柠。
//冒泡排序
function startBubble (arr) {
var length = arr.length;
for ( var i = length; i>= 2 ; --i) {
for ( var j = 0 ; j<=i+1 ; ++j) {
if (arr[j] > arr[j+1]){
[arr[j],arr[j+1]] = [arr[j+1],arr[j]];
}
}
}
}
(2)選擇排序
選擇排序是給每個位置選擇當(dāng)前元素最小的,比如給第一個位置選擇最小的误澳,在剩余元素里面給第二個元素選擇第二小的耻矮,依次類推秦躯,直到第n-1個元素忆谓,第n個元素不用選擇了,因為只剩下它一個最大的元素了踱承。那么倡缠,在一趟選擇,如果當(dāng)前元素比一個元素小茎活,而該小的元素又出現(xiàn)在一個和當(dāng)前元素相等的元素后面昙沦,那么交換后穩(wěn)定性就被破壞了。比較拗口载荔,舉個例子盾饮,序列5 8 5 2 9, 我們知道第一遍選擇第1個元素5會和2交換懒熙,那么原序列中2個5的相對前后順序就被破壞了丘损,所以選擇排序不是一個穩(wěn)定的排序算法。
//選擇排序
function startSelect (arr) {
var length = arr.length;
for ( var i = 0; i <= length-2; ++i ) {
min = i;
for ( var j = i+1;j<length-1 ; ++j ) {
if ( arr[j] < arr[min] ) {
min = i;
}
[arr[j],arr[i]] = [arr[i],arr[j]];
}
}
}
(3)插入排序
插入排序是在一個已經(jīng)有序的小序列的基礎(chǔ)上工扎,一次插入一個元素徘钥。當(dāng)然,剛開始這個有序的小序列只有1個元素肢娘,就是第一個元素呈础。比較是從有序序列的末尾開始,也就是想要插入的元素和已經(jīng)有序的最大者開始比起橱健,如果比它大則直接插入在其后面而钞,否則一直往前找直到找到它該插入的位置。如果碰見一個和插入元素相等的拘荡,那么插入元素把想插入的元素放在相等元素的后面臼节。所以,相等元素的前后順序沒有改變,從原無序序列出去的順序就是排好序后的順序官疲,所以插入排序是穩(wěn)定的袱结。
//插入排序
function startInsert (arr) {
var temp,inner;
var length = arr.length;
for ( var i = 1;i<= length-1 ; ++i) {
temp = arr[i];
inner = i;
while ( inner > 0 && arr[inner-1] >=temp ) {
arr[inner] = arr[inner-1];
--inner;
}
arr[inner]=temp;
}
}
(4)快速排序
快速排序有兩個方向,左邊的i下標(biāo)一直往右走途凫,當(dāng)a[i] <= a[center_index]垢夹,其中center_index是中樞元素的數(shù)組下標(biāo),一般取為數(shù)組第0個元素维费。而右邊的j下標(biāo)一直往左走果元,當(dāng)a[j] > a[center_index]。如果i和j都走不動了犀盟,i <= j, 交換a[i]和a[j],重復(fù)上面的過程而晒,直到i>j。 交換a[j]和a[center_index]阅畴,完成一趟快速排序倡怎。在中樞元素和a[j]交換的時候,很有可能把前面的元素的穩(wěn)定性打亂贱枣,比如序列為 5 3 3 4 3 8 9 10 11监署, 現(xiàn)在中樞元素5和3(第5個元素,下標(biāo)從1開始計)交換就會把元素3的穩(wěn)定性打亂纽哥,所以快速排序是一個不穩(wěn)定的排序算法钠乏,不穩(wěn)定發(fā)生在中樞元素和a[j]交換的時刻。
(5)歸并排序
歸并排序是把序列遞歸地分成短序列春塌,遞歸出口是短序列只有1個元素(認(rèn)為直接有序)或者2個序列(1次比較和交換),然后把各個有序的段序列合并成一個有序的長序列晓避,不斷合并直到原序列全部排好序≈豢牵可以發(fā)現(xiàn)俏拱,在1個或2個元素時,1個元素不會交換吕世,2個元素如果大小相等也沒有人故意交換彰触,這不會破壞穩(wěn)定性。那么命辖,在短的有序序列合并的過程中况毅,穩(wěn)定是是否受到破壞?沒有尔艇,合并過程中我們可以保證如果兩個當(dāng)前元素相等時尔许,我們把處在前面的序列的元素保存在結(jié)果序列的前面,這樣就保證了穩(wěn)定性终娃。所以味廊,歸并排序也是穩(wěn)定的排序算法。
(6)基數(shù)排序
基數(shù)排序是按照低位先排序,然后收集余佛;再按照高位排序柠新,然后再收集;依次類推辉巡,直到最高位恨憎。有時候有些屬性是有優(yōu)先級順序的,先按低優(yōu)先級排序郊楣,再按高優(yōu)先級排序憔恳,最后的次序就是高優(yōu)先級高的在前,高優(yōu)先級相同的低優(yōu)先級高的在前净蚤≡孔椋基數(shù)排序基于分別排序,分別收集今瀑,所以其是穩(wěn)定的排序算法程梦。
(7)希爾排序(shell)
希爾排序是按照不同步長對元素進(jìn)行插入排序,當(dāng)剛開始元素很無序的時候放椰,步長最大作烟,所以插入排序的元素個數(shù)很少,速度很快砾医;當(dāng)元素基本有序了,步長很小衣厘,插入排序?qū)τ谟行虻男蛄行屎芨呷缪痢K裕柵判虻臅r間復(fù)雜度會比o(n^2)好一些影暴。由于多次插入排序错邦,我們知道一次插入排序是穩(wěn)定的,不會改變相同元素的相對順序型宙,但在不同的插入排序過程中撬呢,相同的元素可能在各自的插入排序中移動,最后其穩(wěn)定性就會被打亂妆兑,所以shell排序是不穩(wěn)定的魂拦。
//希爾排序
function XiEr (arr) {
var gaps = [5,3,1];
var gapLength = gaps.length;
var length = arr.length;
for( var x = 0; x < gapLength ; ++x ) {
for ( var i = gaps[x]; i< length ;i++) {
var temp = arr[i];
for ( j = i ;j >= gaps[x] && arr[j-gaps[x]] > temp; j-=gaps[x]) {
arr[j] = arr[j-gaps[x]];
}
arr[j] = temp ;
}
}
}
(8)堆排序
我們知道堆的結(jié)構(gòu)是節(jié)點i的孩子為2i和2i+1節(jié)點,大頂堆要求父節(jié)點大于等于其2個子節(jié)點搁嗓,小頂堆要求父節(jié)點小于等于其2個子節(jié)點芯勘。在一個長為n的序列,堆排序的過程是從第n/2開始和其子節(jié)點共3個值選擇最大(大頂堆)或者最小(小頂堆),這3個元素之間的選擇當(dāng)然不會破壞穩(wěn)定性腺逛。但當(dāng)為n/2-1, n/2-2, ...1這些個父節(jié)點選擇元素時荷愕,就會破壞穩(wěn)定性。有可能第n/2個父節(jié)點交換把后面一個元素交換過去了,而第n/2-1個父節(jié)點把后面一個相同的元素沒有交換安疗,那么這2個相同的元素之間的穩(wěn)定性就被破壞了抛杨。所以,堆排序不是穩(wěn)定的排序算法
快速排序(QuickSort)
快速排序是一個就地排序荐类,分而治之蝶桶,大規(guī)模遞歸的算法。
從本質(zhì)上來說掉冶,它是歸并排序的就地版本真竖。快速排序可以由下面四步組成厌小。
(1) 如果不多于1個數(shù)據(jù)恢共,直接返回。
(2) 一般選擇序列最左邊的值作為支點數(shù)據(jù)璧亚。
(3) 將序列分成2部分讨韭,一部分都大于支點數(shù)據(jù),另外一部分都小于支點數(shù)據(jù)癣蟋。
(4) 對兩邊利用遞歸排序數(shù)列透硝。快速排序比大部分排序算法都要快疯搅。盡管我們可以在某些特殊的情況下寫出比快速排序快的算法濒生,但是就通常情況而言,沒有比它更快的了幔欧∽镏危快速排序是遞歸的,對于內(nèi)存非常有限的機(jī)器來說礁蔗,它不是一個好的選擇觉义。歸并排序(MergeSort)
歸并排序先分解要排序的序列,從1分成2浴井,2分成4晒骇,依次分解,當(dāng)分解到只有1個一組的時候磺浙,就可以排序這些分組洪囤,然后依次合并回原來的序列中,這樣就可以排序所有數(shù)據(jù)屠缭。合并排序比堆排序稍微快一點箍鼓,但是需要比堆排序多一倍的內(nèi)存空間,因為它需要一個額外的數(shù)組呵曹。堆排序(HeapSort)
堆排序適合于數(shù)據(jù)量非常大的場合(百萬數(shù)據(jù))款咖。堆排序不需要大量的遞歸或者多維的暫存數(shù)組何暮。這對于數(shù)據(jù)量非常巨大的序列是合適的。比如超過數(shù)百萬條記錄铐殃,因為快速排序海洼,歸并排序都使用遞歸來設(shè)計算法,在數(shù)據(jù)量非常大的時候富腊,可能會發(fā)生堆棧溢出錯誤坏逢。堆排序會將所有的數(shù)據(jù)建成一個堆,最大的數(shù)據(jù)在堆頂赘被,然后將堆頂數(shù)據(jù)和序列的最后一個數(shù)據(jù)交換是整。接下來再次重建堆,交換數(shù)據(jù)民假,依次下去浮入,就可以排序所有的數(shù)據(jù)。Shell排序(ShellSort)
Shell排序通過將數(shù)據(jù)分成不同的組羊异,先對每一組進(jìn)行排序事秀,然后再對所有的元素進(jìn)行一次插入排序,以減少數(shù)據(jù)交換和移動的次數(shù)野舶。平均效率是O(nlogn)易迹。其中分組的合理性會對算法產(chǎn)生重要的影響。現(xiàn)在多用D.E.Knuth的分組方法平道。Shell排序比冒泡排序快5倍睹欲,比插入排序大致快2倍。
Shell排序比起QuickSort巢掺,MergeSort句伶,HeapSort慢很多。但是它相對比較簡單陆淀,它適合于數(shù)據(jù)量在5000以下并且速度并不是特別重要的場合。它對于數(shù)據(jù)量較小的數(shù)列重復(fù)排序是非常好的先嬉。插入排序(InsertSort)
插入排序通過把序列中的值插入一個已經(jīng)排序好的序列中轧苫,直到該序列的結(jié)束。插入排序是對冒泡排序的改進(jìn)疫蔓。它比冒泡排序快2倍含懊。一般不用在數(shù)據(jù)大于1000的場合下使用插入排序,或者重復(fù)排序超過200數(shù)據(jù)項的序列衅胀。冒泡排序(BubbleSort)
冒泡排序是最慢的排序算法岔乔。在實際運用中它是效率最低的算法。它通過一趟又一趟地比較數(shù)組中的每一個元素滚躯,使較大的數(shù)據(jù)下沉雏门,較小的數(shù)據(jù)上升嘿歌。它是O(n^2)的算法。交換排序(ExchangeSort)和選擇排序(SelectSort)
這兩種排序方法都是交換方法的排序算法茁影,效率都是 O(n2)宙帝。在實際應(yīng)用中處于和冒泡排序基本相同的地位。它們只是排序算法發(fā)展的初級階段募闲,在實際中使用較少步脓。基數(shù)排序(RadixSort)
基數(shù)排序和通常的排序算法并不走同樣的路線。它是一種比較新穎的算法浩螺,但是它只能用于整數(shù)的排序靴患,如果我們要把同樣的辦法運用到浮點數(shù)上,我們必須了解浮點數(shù)的存儲格式要出,并通過特殊的方式將浮點數(shù)映射到整數(shù)上鸳君,然后再映射回去,這是非常麻煩的事情厨幻,因此相嵌,它的使用同樣也不多。而且况脆,最重要的是饭宾,這樣算法也需要較多的存儲空間。