基數排序詳解以及java實現

來源:互聯網
上載者:User

前言

基數排序(radix sort)又稱桶排序(bucket sort),相對於常見的比較排序,基數排序是一種分配式排序,即通過將所有數字分配到應在的位置最後再覆蓋到原數組完成排序的過程。我在上一篇講到的計數排序也屬於這種排序模式,上一篇結尾處提到了計數排序的穩定性,即排序前和排序後相同的數字相對位置保持不變。今天我們要說的基數排序就要利用到排序穩定性這一點。

思考過程

     我們回想一下我們小時候是怎麼學習比較數字大小的?我們是先比位元,如果一個位元比另一個位元多,那這個數肯定更大。如果位元同樣多,就按位元遞減依次往下進行比較,哪個數在這一位上更大那就停止比較,得出這個在這個位上數更大的數字整體更大的結論。當然我們也可以從最小的位開始比較,這其實就對應了基數排序裡的MSD(most significant digital)和LSD(least significant digital)兩種排序方式。

     想清楚了這一點之後,我們就要考慮如何儲存每一位排序結果的問題了,首先既然作為分配式排序,聯想計數排序,每一位排序時儲存該次排序結果的資料結構應該至少是一個長度為10的數組(對應十進位該位0-9的數字)。同時可能存在以下情況:原數組中所有元素在該位上的數字都相同,那一維數組就沒法滿足我們的需要了,我們需要一個10*n(n為數組長度)的二維數組來儲存每次位排序結果。熟悉計數排序結果的讀者可能會好奇:為什麼不能像計數排序一樣,在每個位置只儲存出現該數位次數,而不儲存具體的值,這樣不就可以用一維數組了?這個我們不妨先思考一下,在對基數排序分析完之後再來看這個問題。

      現在我們可以儲存每次位排序的結果了,為了在下一位排序前用到這一位排序的結果,我們要將桶裡排序的結果還原到原數組中去,然後繼續對更改後的原數組執行前一步的位排序操作,如此迴圈,最後的結果就是數組內元素先按最高位排序,最高位相同的依次按下一位排序,依次遞推。得到排序的結果數組。

 

演算法過程

  1. 初始化:構造一個10*n的二維數組,一個長度為n的數組用於儲存每次位排序時每個桶子裡有多少個元素。
  2. 迴圈操作:從低位開始(我們採用LSD的方式),將所有元素對應該位的數字存到相應的桶子裡去(對應二維數組的那一列)。然後將所有桶子裡的元素按照桶子標號從小到大取出,對於同一個桶子裡的元素,先放進去的先取出,後放進去的後取出(保證排序穩定性)。這樣原數組就按該位排序完畢了,繼續下一位操作,直到最高位排序完成。

下面給出一個執行個體協助理解:

我們現有一個數組:73, 22, 93, 43, 55, 14, 28, 65, 39, 81

下面是排序過程(二維數組裡每一列對應一個桶,因為桶空間沒用完,因此沒有將二維數組畫全):

1.按個位排序

0

1

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);    }}}

下面是程式運行結果:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.