排序算法的穩定性

轉載來源:http://www.cnblogs.com/lqminn/p/3642027.html

【DS】排序算法的穩定性

主要的排序算法有八種:直接插入排序,希爾排序(這兩種統稱爲插入排序),冒泡排序,快速排序(這兩種統稱爲交換排序),直接選擇排序,堆排序(這兩種統稱爲選擇排序),歸併排序,基數排序。今天我們就討論一下它們各自的穩定性。如果對算法不熟悉,可以查看我的另外幾篇博客,然後再來閱讀。

一、什麼是算法穩定性

考察排序算法的時候有一個很重要的特性,就是算法的穩定性:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj之前,而在排序後的序列中,ri仍在rj之前,則稱這種排序算法是穩定的;否則稱爲不穩定的。

二、算法穩定性的重要性

算法穩定性爲什麼這麼重要呢?

1)在實際的應用中,我們交換的不一定只是一個整數,而可能是一個很大的對象,交換元素存在一定的開銷;

2)參照基數排序(後面會講),不穩定排序是無法完成基數排序的,講述完基數排序後,還會補充這裏的原因。

三、八大算法的穩定性

1)直接插入排序@排序算法之插入排序(Insertion Sort)

其大致原理是:將數組分爲無序區和有序區兩個區,然後不斷將無序區的第一個元素按大小順序插入到有序區中去,最終將所有無序區元素都移動到有序區完成排序。

我們假設一個數組,元素已經排序爲{1,5A,7,5B,9},其中前面三個已經排序完成,後面沒有排序,即前面三個是有序區,後面兩個是無序區,現在要將無序區的5B插入到有序區,則如果我們將元素插入到5A之前,我們需要往後移動兩個元素,如果插入到5A之後,則需要移動一個元素,因此我們選擇移動一個元素,而5A和5B也保持原來的順序,因而直接插入排序是穩定的。

2)希爾排序@排序算法之希爾排序(Shell Sort)

其大致原理是:又稱Gap縮小排序。先將序列按Gap劃分爲元素個數相同的若干組,使用直接插入排序法進行排序,然後不斷縮小Gap直至爲1,最後使用直接插入排序完成排序。希爾排序其實是直接插入排序的增強版。

我們來證明它是不穩定的,假設有一個數組{3,2A,2B,4},我們要升序排列,按照算法,第一次Gap=2,即可以分爲{3,2B}和{2A,4}兩組,然後對每一組進行插入排序,可以排序成{2B,2A,3,4},第二次Gap=1,由於插入排序是穩定的,所以2A和2B不會交換順序了。由此可以看到,希爾排序是不穩定的。

3)冒泡排序@排序算法之冒泡排序(Bubble Sort)

其大致原理是:將序列劃分爲無序和有序區,不斷通過交換較大元素至無序區尾完成排序。

熟悉冒泡排序的人一定知道,冒泡排序通過不斷的交換元素,將無序區的最大(最小)元素往無序區搬運,因而和插入排序一樣,爲了減少其交換次數,冒泡排序是穩定的。

4)快速排序@排序算法之快速排序(Quick Sort)

其大致原理是:不斷尋找一個序列的中點,將小於該中點的元素搬移到中點左邊,大於該中點的元素搬移到中點右邊,或者反過來。然後對中點左右的序列遞歸的進行排序,直至全部序列排序完成,使用了分治的思想。

關於算法的穩定性有一點本來是打算後面再講的,但是講到快速排序就一定要說了。讀者肯定注意到了,前面的插入排序和冒泡排序完全可以實現爲不穩定算法,只是在比較元素決定是否交換的時候,是否加上等於號而已。快速排序更加顯示了這一點,解釋如下:

在算法導論裏面,快速排序選擇都是元素序列的最後一個元素,假設元素序列如下{3,9,5A,6,8,5B},這種情況下,和上面的情況一下,穩不穩定還是看判斷的時候是否出現等號,但是如果選擇不是這樣的,我們假設一種特殊狀況:{3,9,5A,5B,6,8,5C},算法的實現是選擇中間的5B作爲中點,則不論等號與否,都是不穩定的。實際上,算法導論的選擇是非常有意義的,瞭解其算法過程的人可以看到,這樣的選擇極大的降低了交換元素的複雜度和移動元素的次數。算法導論中是加了等號的,即≤最後一個元素的值被移到了左邊,因而快速排序是穩定的。

5)直接選擇排序@排序算法之選擇排序(Selection Sort)

其大致原理是:將序列劃分爲無序和有序區,尋找無序區中的最小值和無序區的首元素交換,有序區擴大一個,循環最終完成全部排序。

我們還是假設一個序列{1,3,5,10A,10B,7},看這個數列,假設前面三個是有序區,後面三個是無序區,則無序區中最小的元素是7,和無序區的首元素交換10A交換,則可以看到序列變成了{1,3,5,7,10B,10A},然後繼續,無序區就剩下{10B,10A},我們又可以看到,這裏又是一個等號問題,同樣,前面的交換是必然的,而後面的交換(如果等於也要交換)則不是必然的,爲了減少元素交換,直接選擇排序是不穩定的。

6)堆排序

其大致原理是:利用大根堆或小根堆思想,首先建立堆,然後將堆首與堆尾交換,堆尾之後爲有序區。

考慮序列{9,5A,7,5B},按照堆排序的算法走一遍(算法導論中用的是最大堆,這個序列也是用最大堆來設計的),很快就可以發現,輸出序列爲{5B,5A,7,9},而且與等號無關,因此堆排序是不穩定的。

7)歸併排序@排序算法之歸併排序(Merge Sort)

歸併排序請參考
其大致原理是:將原序列劃分爲有序的兩個序列,然後利用歸併算法進行合併,合併之後即爲有序序列。

歸併排序一樣是穩定的,但是歸併排序的穩定性並不是爲了減少元素交換次數,因爲它的算法實現中沒有元素交換這一概念。

8)基數排序

其大致原理是:將數字按位數劃分出n個關鍵字,每次針對一個關鍵字進行排序,然後針對排序後的序列進行下一個關鍵字的排序,循環至所有關鍵字都使用過則排序完成。具體請參見:算法總結系列之五: 基數排序(Radix Sort)

基數排序對多個關鍵字進行排序,並且這些關鍵字還是有優先級別的,對於整數來說,位數越高的數字優先級越高,而基數排序則是對優先級低的先排序,因此,基數排序對於整數是從個十百千萬一個個去排序的。注意,這裏必須使用穩定排序,否則,就會讓原先的地位排序成果毀於一旦,最終的不到正確的排序結果。

基數排序不過是一種思想,其每一位的排序都需要穩定算法,否則無法得到正確的結果。

三、總結

算法穩定性到底爲什麼如此重要?上面提到的八種算法可以看到,其實很多算法都是可以實現穩定和不穩定兩種情形的,那爲什麼選擇穩定?一個基本原因就是減少元素交換次數,但是也有像歸併排序這樣的算法,與交換無關,那麼穩定算法的意義在哪裏呢?

穩定算法在單次排序的時候,意義並不顯著,雖然上面提到減少元素交換,其實鏈表是可以避免這個消耗的,只不過操作比較複雜,其意義顯示在基數排序中,即,我們要對多個關鍵詞多次排序,這個時候,就一定要使用穩定算法。舉一個現實的例子,比如排序的對象是人名,假設有以下兩個人名:

Smith, Alfred
Smith, Zed

我們先按first name排序,再按照last name排序,按照first name排序完成以後,就是上面的樣子,再去按照last name排序,如果算法不穩定,則順序極就會顛倒,是不是?這裏的last name和first name完全可以抽象成基數排序的不同位,不是穩定算法,就不能得到正確結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章