1. 程式人生 > >【資料結構、演算法】八大排序演算法概述(演算法複雜度、穩定性)

【資料結構、演算法】八大排序演算法概述(演算法複雜度、穩定性)

前言

  排序是計算機程式設計中一個非常重要的操作,它將一個數據元素(或記錄)的任意序列重新排列成一個按關鍵字有序的序列。在有序的序列中查詢元素的效率很高,(例如,折半查詢法的平均查詢長度為log2(n+1)1),但是無序序列只能逐一查詢,其平均查詢長度為(n+1)/2。又比如構建二叉排序樹的過程,就是一個排序的過程,因此,如何進行排序,尤其是高效排序,是一個重要的課題。
  排序的資料元素是很多樣的,其關鍵字也是多種的,為了方便理解和表示,後面均使用數列進行分析,其用於排序的關鍵字就是數字本身。(預設將序列排列成升序)

穩定性

  假設某一序列的關鍵字是Ki 

(i=1,2,3,,n),且存在Km=Ks (1m<sn),如果進過排序後,Km依然在Ks左側(前方),則稱該排序方法是穩定的。反之,若Km排列在Ks右側(後方),則稱該排序方法是不穩定的

內部排序與外部排序

  由於排序的記錄數量的不同,使排序過程中設計的儲存器不同,可以將排序分為兩類,第一類是內部排序,指的是待排序記錄存放在計算機自身的儲存器中進行排序的過程;另一類是外部排序,指的是由於待排序記錄數量很大,以致於記憶體無法一次全部容納所有記錄,在排序過程中需要從外存中訪問記錄的排序過程。
在本篇博文中主要分析的都是常見的內部排序演算法,將會盡力用簡短易於理解的語言來分析。

為了方便敘述,首先給出一個無序序列,後續的演算法分析都是基於這個序列。序列如下

初始無序序列

排序演算法

直接插入排序

  直接插入排序(Straight Insertion Sort)是一種非常簡單易理解的排序方法,它的操作是將一個記錄插入到已經排序號好的有序表中。假設上面的序列中,前四個元素經過排序後已經有序,序列為38,49,65,97,再插入76。假設從右往左進行對比,因為65<76<97,所以76被插入到65與97之間。依次對每個元素進行這樣的操作,直到所有的元素都被插入到有序表中。
  在最好的情況下,序列已經是有序的,每次插入元素只需要與有序表中最後一個元素進行比較,時間複雜度為

O(n)。在最壞的情況下,每次插入元素需要與前面所有的元素進行比較,時間複雜度為O(n2),平均時間複雜度為O(n2),演算法的空間複雜度為O(1)。可以想象,在插入第二個49時,當它與前方的49進行比較,兩者相等,不會把第二個放在第一個的前方,所以直接插入排序是穩定的

簡單選擇排序

  簡單選擇排序(Simple Select Sort,也稱為“選擇排序”或者是“直接選擇排序”),其方法是:找出序列中的最小關鍵字,然後將這個元素與序列首端元素交換位置。例如,序列前i個元素已經有序,從第i+1到第n個元素中選擇關鍵字最小的元素,假設第j個元素為最小元素,則交換第j個元素與第i+1個元素的位置。依次執行此操作,直到第n-1個元素也被確定。

這裡寫圖片描述

  相比於上面提到的兩種排序方法,簡單選擇排序不論是否序列已經有序都需要進行n-1次最小數選擇,所以它的最好、最壞以及平均時間複雜度都是O(n2)。演算法的空間複雜度為O(1)。此外,由於交換位置的兩個元素的位置是跳躍的,所以相同關鍵字的元素位置可能發生交換。例如在上面的序列中,第二個49後面還有一個元素1,那麼在第一次排序後,兩個49的排列順序就發生了改變,因此簡單選擇排序是不穩定的

希爾排序

  希爾排序(Shell Sort,又稱“縮小量排序”),它也是一種插入排序,但是在時間效率上有了很大的改進。它的基本思想是:假設序列中有n個元素,首先選擇一個間隔gap,將全部的序列分為gap個子序列,然後分別在子序列內部進行簡單插入排序,得到一個新的主序列;而後縮小gap,再得到子序列,對子序列進行簡單插入排序,又再次得到新的主序列,直到gap=1為止。在演算法中,排序前期,由於gap值比較大,每個子序列中元素少,排序快,到了排序後期,由於前面的排序導致序列已經基本有序,排序速度也很快。
  希爾排序的時間複雜度與gap的選擇有很大的關係,一般時間複雜度是低於O(n2)。演算法的空間複雜度為O(1)。由於希爾排序中元素交換位置也是跳躍式的,所以它也是不穩定的

這裡寫圖片描述

氣泡排序

  氣泡排序(Bubble Sort)也是一種簡單的排序演算法,其方法是:首先將第一個關鍵字與第二個關鍵字進行比較,若逆序,則交換位置;然後比較第二個與第三個關鍵字,依次進行比較,直到第n-1個關鍵字和第n的關鍵字完成比較為止。上述過程是第一趟冒泡,結果就是值最大的關鍵字排在了最後面。然後對前n-1個關鍵字進行第二趟冒泡,如此往復,直到整個序列有序為止。很明顯,若果某一趟冒泡過程中沒有發生元素位置交換,那麼此時整個序列已經是有序的,無需繼續下面的操作。
  在最好的情況下,序列已經是有序的,只進行了第一趟冒泡比較,此時演算法的時間複雜度為O(n)。在最壞的情況下,執行了n-1次冒泡,時間複雜度為O(n2)。演算法的空間複雜度為O(1)。在比較上面序列中的第一個49與第二個49時,兩者相等,不會進行位置交換,所以演算法是穩定的

快速排序

  快速排序(Quick Sort),快速排序是對氣泡排序的一種改進,它的基本思想如下:通過一趟排序將序列分割成兩部分,其中一部分的關鍵字均大於另一部分,則可以分別對兩部分進行排序,從而使整個序列有序。
  實施方案是:首先選擇一個關鍵字作為樞紐(通常為第一個元素的關鍵字),然後讓所有關鍵字比樞紐小元素放在樞紐前面,比樞紐大的元素放在樞紐後面。這樣就根據樞紐最後落在的位置將序列分割成為了兩部分。然後進行遞迴運算,就可以對數列進行排序。
  例如用快速排序上面的序列。起初以49為樞紐,用兩個指標分別指向第0個和最後一個元素(i=0,j=7)。從後往前搜尋,搜尋至27時(i=0,j=6),由於27<49,交換兩個元素的位置,序列變成27、38、65、97、76、13、49、49,這時(i=0,j=6);從前往後搜尋,搜尋i=2時,65>49,則交換元素的位置,序列變成27、38、49、97、76、13、65、49,此時(i=2,j=6);再從後往前搜尋,搜尋至j=5時,13<49, 則交換元素的位置,序列變成27、38、13、97、76、49、65、49,此時(i=3,j=5),從前往後搜尋,搜尋至i=4時,97>49,交換元素的位置,序列變成27、38、13、49、76、97、65、49,此時(i=4,j=5),再從後往前搜尋,由於i=j=4時不會發生交換,所以第一趟搜尋結束,此時以49為分界,49前方的數字均大於49後方的數字。然後分別對49的左右兩部分進行遞迴排序。
  快速排序演算法遞迴的次數取決於元素的數目,最理想的情況下,每次劃分左右兩部分的長度相等,則需要遞迴次(nlogn)次,每次需要比較定位的次數為n,所以最理想的時間複雜度為O(nlogn)。可以證明平均時間複雜度也為O(nlogn),最壞的情況下它可以被看做氣泡排序,時間複雜度為O(n2)。空間複雜度是遞迴過程中的需要佔據的空間,最優情況下空間複雜度為O(nlogn),很明顯它是不穩定的

堆排序

  對於有n個元素的關鍵字序列k1,k2,k3,,kn,當且僅當所有關鍵字滿足以下條件時稱之為最小堆或者最大堆。

這裡寫圖片描述

  前者稱之為最小堆,後者稱之為最大堆。前者任一節點的關鍵之均小於其左右孩子的關鍵字,後者任一節點的關鍵之均大於其左右孩子的關鍵字,下面以小堆為例進行講解。
  首先根據上面的數列的關鍵字構建一個堆,然後找到從下往上的第一個非葉子結點97,對比其與其左右孩子的大小,若有孩子比起小則交換位置。

這裡寫圖片描述

  然後找到下一個非葉子結點,對比其葉子結點,進行位置交換,這裡將65與13交換位置。

這裡寫圖片描述

  依次對比,直到形成最小堆,此時堆頂為13,輸出13,或者將第一位數與最後一位數交換位置。

這裡寫圖片描述

  如此往復,直到輸出所有元素為止。

  堆排序的時間複雜度為O(nlogn),需要一個臨時空間用於交換元素,所以空間複雜度為O(1),堆排序的位置交換也是跳躍式的,是一個不穩定的排序方法。

基數排序

  基數排序的思想是按照組成關鍵字的各個數位進行排序,它是分配排序的一種。假如關鍵字是十進位制數字,那麼令r=10,d是所有關鍵字中的最大位數(位數小於d的數字,在前方補0)。基數排序可以從最低有效位開始,也可以從最高有效位開始。
  基數排序的思想是:設立r個佇列,編號分別為0,1,2,3…,r-1。首先按照最低有效位的值,將n個關鍵字放置到r個佇列中,然後從小到大將元素收集起來,再按照次低位的值將元素放置到各個佇列中,再進行收集,重複上述過程,直到收集完畢為止。
這裡寫圖片描述

  對於有n個元素的序列,執行一次放置和收集的時間為O(n+r),則其時間複雜度為
O(d(n+r)),空間複雜度為O(dr),雖然基數排序的位置交換是跳躍的,但是元素放置和收集時都是按照順序進行的,所以不會打亂順序,是一種穩定的演算法。

歸併排序

  之前有寫過一篇歸併排序的博文,不理解的可以到《【資料結構】二叉樹的遍歷及應用(https://blog.csdn.net/u013921430/article/details/80252035)》去閱讀,講解的很詳細。歸併排序的時間複雜度為O(nlogn),空間複雜度為O(n),是一種穩定的排序演算法。

總結

這裡寫圖片描述
  各個演算法的時間複雜度、空間複雜度及穩定性的表格如上圖所示(偷個懶,找了本數,把書上的內容拍下來了。)。迄今為止,已有的排序