基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較
基數排序是一種穩定排序算法,在某些時候,基數排序的效率高於其它的穩定排序
基數排序的方式可以採用最低位優先LSD(Least sgnificant digital)法或最高位優先MSD(Most sgnificant digital)法,LSD就是從個位先進行排序,然後十位、百位......MSD則相反
舉例分析
有一組無序序列,我們將其用基數排序算法進行排序(LSD)
第一次排序
- 我們按個位上的數字來進行劃分
- 個位上的數字爲0,就放入第0個桶中,個位上的數字爲1,就放入第一個桶中......
- 按個位劃分之後,我們再將這些數據依次取出來(取出之後已經是個位上的數字有序)
第二次排序
- 然後再將以上數據,按十位上的數字大小進行劃分
- 按十位劃分之後,我們再將這些數據依次取出來(取出之後已經是個位上的數字和十位上的數字都有序)
第三次排序
- 然後再將以上數據,按百位上的數字大小進行劃分
- 按百位劃分之後,我們再將這些數據依次取出來(取出之後已經是個位上的數字、十位上的數字、百位上的數字都有序)
- 排序結束,此時已經是一個有序序列
在以上排序中我們可以看到,共進行了三次排序,爲什麼?
因爲整個數組中的元素,最大元素就是999,他是3位的,所以需要進行3次排序
假如最大元素時1314,是4位的,那麼我們就需要進行4次排序
總結一下
- 先找到數組中最大元素的位數,判斷要進行幾次排序
- 然後先排個位,再排十位......
- 其中每趟排序中,先依次劃分元素,然後再依次取出元素
注意:劃分元素和取出元素時,一定是按順序進行的,纔可保證排序成功(因爲寫代碼的時候,肯定是按順序遍歷的,所以無需特殊操作,只需正常寫即可)
Java代碼
package algorithm;
import java.util.Arrays;
public class Sort {
// 基數排序
private static int[] radixSort(int array[]) {
// 10個桶,每個桶最多放全部
int[][] temp = new int[10][array.length];
// 存放每個桶裏面放了幾個元素
int[] tempIndex = new int[10];
// 獲得數組中最大的數字
int max = getMax(array);
// 獲得最大數字的位數,
int maxLength = (max+"").length(); // 轉換成字符串,再獲取長度即可
// 共比較最大數字位數次
for (int i = 0, n = 1; i < maxLength ; i++, n *= 10) {
// 一次排序,將對應元素放入對應數組中
for (int j = 0 ; j < array.length ; j++) {
// 取個位:(520 / 1) % 10 = 0
// 取十位:(520 / 10) % 10 = 2
// 取百位:(520 / 100) % 10 = 5
// 所以第一次比較個位 n = 1,第二次比較十位 n = 10...
int remainder = (array[j] / n) % 10;
// 把當前元素放入指定數組中的指定位置
// 取出來的餘數是幾,就放入第幾個桶中(數組的第幾行)
// 然後放入那個桶中的 tempIndex[remainder] 位置
temp[remainder][tempIndex[remainder]] = array[j];
tempIndex[remainder]++;
}
// 用於取出數據時放入原數組中的下標
int index = 0;
// 將元素全部依次取出
for (int k = 0 ; k < tempIndex.length ; k++) { // k 代表第幾個桶
if (tempIndex[k] != 0) { // 說明這個桶中放入了元素,將其取出
// 取出一個桶中的數據
for (int w = 0 ; w < tempIndex[k] ; w++) {
array[index] = temp[k][w]; // 替換原數組元素
index++;
}
tempIndex[k] = 0; // 清零
} // 取完一個桶的元素
} // 取完全部桶的元素
}
return array;
}
// 獲得數組中最大的數字
private static int getMax(int array[]) {
int max = 0;
for (int anArray : array) {
if (anArray > max) {
max = anArray;
}
}
return max;
}
public static void main(String[] args) {
int[] array = new int[]{5, 2, 0, 13, 14, 520, 134, 1314, 9999};
int[] result = radixSort(array);
System.out.println(Arrays.toString(result));
}
}
在以上代碼中,我們需要注意一下創建二維數組這個
// 10個桶,每個桶最多放全部
int[][] temp = new int[10][array.length];
數組的大小是10 × array.length ,說明有十行, array.length 列,每行代表一個桶,而一行中有多少個列,就代表這個桶可以放多少個元素
- 因爲我們並不知道個位爲0的有多少個元素,個位爲1的有多少個元素,十位爲9的有多少個元素......
- 所以我們創建數組時,有多少列是不確定的
- 而列的最大數,就是當所有元素的某位上數字都是一樣的時候,就是數組的長度
- 所以我們這裏將其設置爲最大 array.length
如果數據少的情況下還好,但是當數據量很大的情況,爲數組指定很大的空間,而根本用不到這麼多的空間,就會有很多的空間被浪費掉了
所以我們可以先爲數組指定一些空間,如果空間不夠的話,再進行數組擴容,這樣就有效避免資源浪費
或者直接存入一維數組中,比如在遍歷個位的時候,直接將元素放入新的一維數組的指定位置中,而新的數組大小我們可以直接指定爲原數組的大小
這裏爲了看起來更直觀,所以採用二維數組模擬放置的桶(不是桶排序)
基數排序是一種用空間換時間的排序算法
時間複雜度
最好情況:O(k * N),k 爲數組中最大數的位數
平均情況:O(k * N)
最壞情況:O(k * N)
雖然同爲基數排序,但不同的寫法,時間複雜度也會有一些差異