主要的排序算法有八種:直接插入排序,希爾排序(這兩種統稱爲插入排序),冒泡排序,快速排序(這兩種統稱爲交換排序),直接選擇排序,堆排序(這兩種統稱爲選擇排序),歸併排序,基數排序。今天咱們就討論一下它們各自的穩定性。若是對算法不熟悉,能夠查看個人另外幾篇博客,而後再來閱讀。html
1、什麼是算法穩定性算法
考察排序算法的時候有一個很重要的特性,就是算法的穩定性:假定在待排序的記錄序列中,存在多個具備相同的關鍵字的記錄,若通過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj以前,而在排序後的序列中,ri仍在rj以前,則稱這種排序算法是穩定的;不然稱爲不穩定的。數組
2、算法穩定性的重要性post
算法穩定性爲何這麼重要呢?ui
1)在實際的應用中,咱們交換的不必定只是一個整數,而多是一個很大的對象,交換元素存在必定的開銷;url
2)參照基數排序(後面會講),不穩定排序是沒法完成基數排序的,講述完基數排序後,還會補充這裏的緣由。設計
3、八大算法的穩定性code
1)直接插入排序@排序算法之插入排序(Insertion Sort)htm
其大體原理是:將數組分爲無序區和有序區兩個區,而後不斷將無序區的第一個元素按大小順序插入到有序區中去,最終將全部無序區元素都移動到有序區完成排序。對象
咱們假設一個數組,元素已經排序爲{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)
基數排序對多個關鍵字進行排序,而且這些關鍵字仍是有優先級別的,對於整數來講,位數越高的數字優先級越高,而基數排序則是對優先級低的先排序,所以,基數排序對於整數是從個十百千萬一個個去排序的。注意,這裏必須使用穩定排序,不然,就會讓原先的地位排序成果毀於一旦,最終的不到正確的排序結果。
基數排序不過是一種思想,其每一位的排序都須要穩定算法,不然沒法獲得正確的結果。
3、總結
算法穩定性到底爲何如此重要?上面提到的八種算法能夠看到,其實不少算法都是能夠實現穩定和不穩定兩種情形的,那爲何選擇穩定?一個基本緣由就是減小元素交換次數,可是也有像歸併排序這樣的算法,與交換無關,那麼穩定算法的意義在哪裏呢?
穩定算法在單次排序的時候,意義並不顯著,雖然上面提到減小元素交換,其實鏈表是能夠避免這個消耗的,只不過操做比較複雜,其意義顯示在基數排序中,即,咱們要對多個關鍵詞屢次排序,這個時候,就必定要使用穩定算法。舉一個現實的例子,好比排序的對象是人名,假設有如下兩我的名:
Smith, Alfred Smith, Zed
咱們先按first name排序,再按照last name排序,按照first name排序完成之後,就是上面的樣子,再去按照last name排序,若是算法不穩定,則順序極就會顛倒,是否是?這裏的last name和first name徹底能夠抽象成基數排序的不一樣位,不是穩定算法,就不能獲得正確結果。