基數排序(radix sort)又稱桶排序(bucket sort),相對於常見的比較排序,基數排序是一種分配式排序,即經過將全部數字分配到應在的位置最後再覆蓋到原數組完成排序的過程。我在上一篇講到的計數排序也屬於這種排序模式,上一篇結尾處提到了計數排序的穩定性,即排序前和排序後相同的數字相對位置保持不變。今天咱們要說的基數排序就要利用到排序穩定性這一點。html
咱們回想一下咱們小時候是怎麼學習比較數字大小的?咱們是先比位數,若是一個位數比另外一個位數多,那這個數確定更大。若是位數一樣多,就按位數遞減依次往下進行比較,哪一個數在這一位上更大那就中止比較,得出這個在這個位上數更大的數字總體更大的結論。固然咱們也能夠從最小的位開始比較,這其實就對應了基數排序裏的MSD(most significant digital)和LSD(least significant digital)兩種排序方式。java
想清楚了這一點以後,咱們就要考慮如何存儲每一位排序結果的問題了,首先既然做爲分配式排序,聯想計數排序,每一位排序時存儲該次排序結果的數據結構應該至少是一個長度爲10的數組(對應十進制該位0-9的數字)。同時可能存在如下狀況:原數組中全部元素在該位上的數字都相同,那一維數組就無法知足咱們的須要了,咱們須要一個10*n(n爲數組長度)的二維數組來存儲每次位排序結果。熟悉計數排序結果的讀者可能會好奇:爲何不能像計數排序同樣,在每一個位置只存儲出現該數字的次數,而不存儲具體的值,這樣不就能夠用一維數組了?這個咱們不妨先思考一下,在對基數排序分析完以後再來看這個問題。git
如今咱們能夠存儲每次位排序的結果了,爲了在下一位排序前用到這一位排序的結果,咱們要將桶裏排序的結果還原到原數組中去,而後繼續對更改後的原數組執行前一步的位排序操做,如此循環,最後的結果就是數組內元素先按最高位排序,最高位相同的依次按下一位排序,依次遞推。獲得排序的結果數組。算法
下面給出一個實例幫助理解:數組
咱們現有一個數組:73, 22, 93, 43, 55, 14, 28, 65, 39, 81數據結構
下面是排序過程(二維數組裏每一列對應一個桶,由於桶空間沒用完,所以沒有將二維數組畫全):學習
1.按個位排序spa
0code |
1htm |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
|
81 |
22 |
73 |
14 |
55 |
|
|
28 |
39 |
|
|
|
93 |
|
65 |
|
|
|
|
|
|
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
按第一位排序後數組結果:
81,22,73,93,43,14,55,65,28,39
能夠看到數組已經按個位排序了。
2根據個位排序結果按百位排序
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
|
14 |
22 |
39 |
43 |
55 |
65 |
73 |
81 |
93 |
|
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
取出排序結果:
14,22,28,39,43,55,65,73,81,93
能夠看到在個位排序的基礎上,百位也排序完成(對於百位相同的數子,如22,28,由於個位已經排序,而取出時也保持了排序的穩定性,因此這兩個數的位置先後是根據他們個位排序結果決定的)。由於原數組元素最高只有百位,原數組也完成了排序過程。
咱們如今來看看以前遺留的兩個問題:爲何不能用一維數組,必定要用二維數組這樣的相似桶的結構來存儲中間位排序結果?其實之因此要寫這個問題,是由於我以爲這個問題是理解基數排序的關鍵。基數排序自己原理很簡單,可是實現中有兩個問題須要考慮:1.怎麼保留前一位的排序結果,這個問題用以前提到的排序穩定性能夠解決。2.怎麼關聯該位排序結果和原數組元素,二維數組正是爲了解決這個問題使用的辦法。在計數排序裏,雖然保留了全部相等的元素的相對位置,可是這些相等的元素在計數排序裏實際是沒有差異的,所以咱們能夠只保存數組裏有多少個這樣的元素便可。而基數排序裏不一樣,有些元素雖然在某一位上相同,可是他們其餘位上極可能不一樣,若是隻保存該位上有多少個5或者多少個6,那關於元素其餘位的信息就都丟棄了,這樣也就無法對這些元素更高位進行排序了。
弄清基數排序的過程後,咱們來看看這個算法的時間複雜度是多少?每次循環遍歷數組將元素放在指定位置Θ(n),在從桶中取出數據Θ(n),循環d次(d是位數),時間複雜度就是Θ(r*n)
最後附上基數排序的java實現:
package sort; public class RadixSort { private static void radixSort(int[] array,int d) { int n=1;//表明位數對應的數:1,10,100... int k=0;//保存每一位排序後的結果用於下一位的排序輸入 int length=array.length; int[][] bucket=new int[10][length];//排序桶用於保存每次排序後的結果,這一位上排序結果相同的數字放在同一個桶裏 int[] order=new int[length];//用於保存每一個桶裏有多少個數字 while(n<d) { for(int num:array) //將數組array裏的每一個數字放在相應的桶裏 { int digit=(num/n)%10; bucket[digit][order[digit]]=num; order[digit]++; } for(int i=0;i<length;i++)//將前一個循環生成的桶裏的數據覆蓋到原數組中用於保存這一位的排序結果 { if(order[i]!=0)//這個桶裏有數據,從上到下遍歷這個桶並將數據保存到原數組中 { for(int j=0;j<order[i];j++) { array[k]=bucket[i][j]; k++; } } order[i]=0;//將桶裏計數器置0,用於下一次位排序 } n*=10; k=0;//將k置0,用於下一輪保存位排序結果 } } public static void main(String[] args) { int[] A=new int[]{73,22, 93, 43, 55, 14, 28, 65, 39, 81}; radixSort(A, 100); for(int num:A) { System.out.println(num); } } }
下面是程序運行結果: