1. 程式人生 > >排序演算法——穩定性、比較次數、交換次數

排序演算法——穩定性、比較次數、交換次數

 本文轉自:無名大盜——http://blog.csdn.net/dreamer2020/article/details/8740244

在學習排序演算法時,出於效率考慮,經常容易看到演算法的穩定性、比較次數及交換次數研究。特別是考試或者公司筆試題,經常出現這樣的題目。由於排序演算法有很多種,平時提出大家才能說出個大概,但真要考查這些細節,估計很多人都說不準確。博主下決心寫此文章,徹底探查清楚這些問題,與大家共享之。

        首先說明穩定性是指相同元素在排序後相對位置保持不變。個人感覺穩定性的含義在於更廣泛情形下,排序元素通常具有多個鍵值,即可以按照多個標準來排序。穩定性則保證了按照一個鍵排序的結果可以為第二個鍵所用。舉個例子,對於學生的課程成績,通常會和學號、姓名列在一起,先按照學生學號排序,然後再根據成績從高到低,這樣,相同分數的學生則是按照學號排名。

        其次是關於比較次數和交換次數,通常用於演算法效率分析。基於比較的排序演算法其效能最好是nlog(n)。因而,不同的優化都是向這個邊界靠近。且不同的演算法也有不同的應用場景,不完全是看複雜度。

        下面逐個分析常見排序演算法。

一、氣泡排序

        氣泡排序的原理是將相鄰元素比較,小的往左移動,大的往右,整個過程就像是水中氣泡上浮。在相鄰兩個元素的比較中,如果相等,則沒有必要交換。這一點,保證了氣泡排序的穩定性。無論相等的元素之前處於什麼位置,在冒泡的效果下, 最終會相鄰,只要相等元素不交換,就不會改變相對位置。所以氣泡排序是穩定的。

對於n個元素,相鄰元素均要比較,共有(n-1)次。經過一回合冒泡過程後,最大元素沉澱到最右位置。第二回合, 只剩下(n-1)個元素,只需要比較(n-2)次。依次類推,其他比較次數為(n-3),......,2,1. 所以總共比較次數為n(n-1)/2,而且是固定為這個數目.

       至於交換次數,這個取決於初始序列的逆序數。對於陣列A[1,...,n],如果對於i<j有A[i]>A[j],則稱(A[i],A[j])是一個逆序對,序列中逆序對的個數稱為逆序數。

氣泡排序每次交換,只改變了相鄰兩元素的位置,不影響和其他元素之間的逆序關係,因而,逆序數只減1。所以,氣泡排序交換次數等於初始序列的逆序數。

二、選擇排序

        選擇排序的原理是每回合找出最小元素,然後交換到前面位置。

        選擇排序是不穩定的排序演算法,不穩定主要產生於交換。交換過程可能改變相同元素的相對位置,舉個例子,序列(5,8,5,1),最小數是1,第一次交換,得到(1,8,5

,5),元素5相對位置已經發生變化。

        下面是比較次數。對於n個元素的序列,找出最小元素需要比較(n-1)次。第一回合後,序列只剩下(n-1)個元素,下一次找最小元素還需要(n-2)次比較。最後直到2個元素需要比較1次。所以最後比較次數總共為(n-1)+(n-2)+...+1=n(n-1)/2,且固定不變。

        每一回合最多交換一次,有(n-1)回合,所以最多交換次數為(n-1)。

三、插入排序

        插入排序基本原理是假定前面i個元素已經排好,接下來將第(i+1)個元素插入到前面的序列中,保證有序。迴圈插入所有元素,即完成排序。

插入第(i+1)元素如果是從後往前掃描,尋找比其小的元素,這叫作直接插入排序。如果是使用二分查詢判斷新元素位置,則叫作二分插入排序。兩種插入排序都是穩定的。對於直接插入排序,新來元素位置是通過從後往前比較(尋找比其小或相等元素,假設是a[j])來確定的,將新元素放在a[j]後即可,相等可以保證穩定性。對於二分插入排序,可以通過修改二分查詢來保證穩定性,如下程式碼所示,但x[mid] == temp時,low指標右移,該操作可以保證相等元素不會被放到前面。

  1. while (low <= high)     
  2.    {     
  3.     mid = (low + high) / 2;     
  4.     if (x[mid] <= temp)     
  5.     {     
  6.      low = mid + 1;     
  7.     }     
  8.     else
  9.     {     
  10.      high = mid - 1;     
  11.     }   

直接插入排序每回合的比較次數和元素移動次數等於其原始位置和插入位置之間的偏移。最好情況下(有序),需要比較(n-1)次,移動0次;最差情況下,需要比較1+2+...+(n-1)=n(n-1)/2次,移動n(n-1)/2次。在程式實現上,通常會用一個臨時變數(如temp)儲存待插入元素,最後又將temp移動到相應位置上,因而在很多教材上第回合插入會多2次移動操作,本文在此指出這一點。從上面的分析可以發現,直接插入排序對於基本有序的初始序列,有較好效果,無論是比較次數還是移動次數,都很少。

二分插入排序僅僅是加快了查詢這一過程,即減少了元素比較次數,對於m個有序的序列,二分查詢最壞情況下比較次數為1+log(m)。因而,在插入排序中,元素比較次數為(n-1)+log(1*2*...*(n-1)),在初始序列雜亂無章的情形下,其平均查詢效能較好。

四、歸併排序

歸併的基本思想是合併多路有序陣列,通常我們考慮兩路歸併演算法。

歸併排序是穩定的,這是因為,在進行多路歸併時,如果各個序列當前元素均相等,可以令排在前面的子序列的元素先處理,不會打亂相等元素的順序。

考慮元素比較次數,兩個長度分別為m和n的有序陣列,每次比較處理一個元素,因而合併的最多比較次數為(m+n-1),最少比較次數為min(m,n)。對於兩路歸併,序列都是兩兩合併的。不妨假設元素個數為n=2^h,

第一次歸併:合併兩個長度為1的陣列,總共有n/2個合併,比較次數為n/2。

第二次歸併:合併兩個長度為2的陣列,最少比較次數是2,最多是3,總共有n/4次,比較次數是(2~3)n/4。

第三次歸併:合併兩個長度為4的陣列,最少比較次數是4,最多是7,總共有n/8次合併,比較次數是(4-7)n/8。

第k次歸併:合併兩個長度為2^(k-1)的陣列,最少比較次數是2^(k-1),最多是2^k-1,總共合併n/(2^k)次,比較次數是[2^(k-1)~(2^k-1)](n/2^k)=n/2~n(1-1/2^k)。

按照這樣的思路,可以估算比較次數下界為n/2*h=nlog2(n)/2。上界為n[h-(1/2+1/4+1/8+...+1/2^h)]=n[h-(1-1/2^h)]=nlog2(n)-n+1。

綜上所述,歸併排序比較次數為nlog2(n)/2~nlog2(n)-n+1。

       歸併排序引入了一個與初始序列長度相同的陣列來儲存合併後的結果,因而不涉及交換。

五、快速排序

快速排序的基本思想是分治。

        快排是不穩定的,關鍵在於劃分過程。現有的幾種劃分過程,基本都是分兩個指標從左右同時掃描,然後交換元素,交換過程很容易打亂元素位置。

        元素比較次數,也就是其複雜性分析。理想情況下,快速排序每次劃分都將原始序列劃分為兩個等長的子序列。所以其比較次數為T(n)=2T(n/2)+n,所以其平均期望時間為nlog(n)。但在最壞情況下,即序列有序情形下,每次劃分只能分出一個元素,因而總共需要(n-1)次劃分,總的比較次數為(n-1)+(n-2)+...+1=n(n-1)/2,即退化為O(n^2).

        元素交換次數取決於序列,不知道怎麼給出準確分析,只好略過。

六、堆排序

堆排序的基本思想是對序列建立最小堆,然後依次取堆頂元素、刪除和調整堆。

       堆排序是不穩定的,堆的刪除操作直接將最後一個元素提到堆頂,然後再調整,該操作容易改變相同元素的順序。

比較主要發生在堆調整過程中,堆排序可以分解為兩個過程,一是建堆,二是移除元素。建堆過程中,自底而上,每個元素和其孩子結點比較,找最大元素,並調整。如果元素是從大到小排列的,即已經成堆的情形下,對於完全樹,其比較次數為(n-1)。在不滿足堆的前提下,還會發生遞迴呼叫,比較次數更多。調整過程中,把最後一個元素提到堆頂後,需要重新調整堆,此時還是需要比較和調整。因而,初始有序的序列,其比較次數不會有太大變化(此處有疑問,無法論證清楚)。所以,堆排序的比較次數較穩定的靠近nlog(n)。

       堆調整過程中會發生交換,交換次數跟資料位置相關,目前沒有見到有分析清楚這個的。

七、其他排序演算法

除上述外,還有許多其他排序演算法,如希爾排序、基數排序、桶排序等。

       希爾排序將序列劃分成多個子序列,先對子序列分別排序,然後減少子序列個數,重複該過程。在開始時,資料規模較小,到最後,資料多數有序,採用直接插入排序,整體來說,取得比較好的效果。

     基數排序根據多個鍵值對序列進行分配,屬於分配類演算法。

最後,本文分析可以總結如下:


穩定性 比較次數 交換次數 空間複雜性 適用場景
氣泡排序 穩定 n(n-1)/2 逆序數 O(1)
選擇排序 不穩定 n(n-1)/2 0~(n-1) O(1)
插入排序 穩定 最好為(n-1),最差為n(n-1)/2 最好為0,最差為n(n-1)/2 O(1) 初始序列大量有序
歸併排序 穩定 nlog2(n)/2~nlog2(n)-n+1 O(n) 大量資料排序,外排序
快速排序 不穩定 nlog(n),最差為n(n-1)/2 無法分析 log(n)
堆排序 不穩定 nlog(n),和初始順序關係不大 無法分析 O(1)