常用排序演算法總結(2)

發表於2018-05-13

上一篇總結了常用的比較排序演算法,主要有氣泡排序選擇排序插入排序歸併排序堆排序快速排序等。

這篇文章中我們來探討一下常用的非比較排序演算法:計數排序基數排序桶排序。在一定條件下,它們的時間複雜度可以達到O(n)。

這裡我們用到的唯一資料結構就是陣列,當然我們也可以利用連結串列來實現下述演算法。

計數排序(Counting Sort)

計數排序用到一個額外的計數陣列C,根據陣列C來將原陣列A中的元素排到正確的位置。

通俗地理解,例如有10個年齡不同的人,假如統計出有8個人的年齡不比小明大(即小於等於小明的年齡,這裡也包括了小明),那麼小明的年齡就排在第8位,通過這種思想可以確定每個人的位置,也就排好了序。當然,年齡一樣時需要特殊處理(保證穩定性):通過反向填充目標陣列,填充完畢後將對應的數字統計遞減,可以確保計數排序的穩定性。

計數排序的步驟如下:

  1. 統計陣列A中每個值A[i]出現的次數,存入C[A[i]]
  2. 從前向後,使陣列C中的每個值等於其與前一項相加,這樣陣列C[A[i]]就變成了代表陣列A中小於等於A[i]的元素個數
  3. 反向填充目標陣列B:將陣列元素A[i]放在陣列B的第C[A[i]]個位置(下標為C[A[i]] – 1),每放一個元素就將C[A[i]]遞減

計數排序的實現程式碼如下:

下圖給出了對{ 4, 1, 3, 4, 3 }進行計數排序的簡單演示過程

計數排序的時間複雜度和空間複雜度與陣列A的資料範圍(A中元素的最大值與最小值的差加上1)有關,因此對於資料範圍很大的陣列,計數排序需要大量時間和記憶體。

例如:對0到99之間的數字進行排序,計數排序是最好的演算法,然而計數排序並不適合按字母順序排序人名,將計數排序用在基數排序演算法中,能夠更有效的排序資料範圍很大的陣列。

  基數排序(Radix Sort)

基數排序的發明可以追溯到1887年赫爾曼·何樂禮在打孔卡片製表機上的貢獻。它是這樣實現的:將所有待比較正整數統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位開始進行基數為10的計數排序,一直到最高位計數排序完後,數列就變成一個有序序列(利用了計數排序的穩定性)。

基數排序的實現程式碼如下:

下圖給出了對{ 329, 457, 657, 839, 436, 720, 355 }進行基數排序的簡單演示過程

基數排序的時間複雜度是O(n * dn),其中n是待排序元素個數,dn是數字位數。這個時間複雜度不一定優於O(n log n),dn的大小取決於數字位的選擇(比如位元位數),和待排序資料所屬資料型別的全集的大小;dn決定了進行多少輪處理,而n是每輪處理的運算元目。

如果考慮和比較排序進行對照,基數排序的形式複雜度雖然不一定更小,但由於不進行比較,因此其基本操作的代價較小,而且如果適當的選擇基數,dn一般不大於log n,所以基數排序一般要快過基於比較的排序,比如快速排序。由於整數也可以表達字串(比如名字或日期)和特定格式的浮點數,所以基數排序並不是只能用於整數排序。

  桶排序(Bucket Sort)

桶排序也叫箱排序。工作的原理是將陣列元素對映到有限數量個桶裡,利用計數排序可以定位桶的邊界,每個桶再各自進行桶內排序(使用其它排序演算法或以遞迴方式繼續使用桶排序)。

桶排序的實現程式碼如下:

下圖給出了對{ 29, 25, 3, 49, 9, 37, 21, 43 }進行桶排序的簡單演示過程

桶排序不是比較排序,不受到O(nlogn)下限的影響,它是鴿巢排序的一種歸納結果,當所要排序的陣列值分散均勻的時候,桶排序擁有線性的時間複雜度。

相關文章