基數排序(桶排序)
1.定義:
基數排序(radix sort)屬於“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度爲O (nlog®m),其中r爲所採取的基數,而m爲堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法(截取自百度百科)。
2.原理
/**
* 基數排序的思想
* 首先定義10個桶 ,然後對數組中的數據進行按個位、十位、百位、千位 來分配到相應的桶中
* 1.比如: arr這樣一個數組數據 那麼第一輪按照每位數據的個位數進行分桶
* 桶的位數 0 1 2 3 4 5 6 7 8 9
* 數據: 32 3 54 256
* 724 16
* 經過第一輪排序後位: 32,3,54,724,256,16
* 2. 第二輪按照十位來分
* 桶的位數 0 1 2 3 4 5 6 7 8 9
* 數據: 3 16 724 32 54
* 256
* 進過第二輪排序後: 3,16,724,32,54,256
* 3.第三輪按照百位來分
* 桶的位數 0 1 2 3 4 5 6 7 8 9
* 數據: 3 256 724
* 16
* 32
* 54
* 進過第三輪排序後: 3,16,32,54,256,724
* 說明:
* 1.排序的次數按照數組中最大位數來的
* 2.當位數不足時,在位數前補0
*
* 需注意:
* 1.定義10個桶 剛好對應0-9這個10個數字 然後每個桶需要記錄桶裏面數據的個數
3.代碼實現
下面是每一步推導的過程,結合上面的算法思想去理解就會很容易了。
public static void radixSort(int[] arr) {
//首先需要定義桶的數量
int [][] msg=new int[10][arr.length];
//用來記錄每個桶裝了幾個數據
int [] backElment=new int[10];
for(int j=0;j<arr.length;j++) {
//取出個位數據
int temp=arr[j]/1 % 10;
//放到對應的桶中
msg[temp][backElment[temp]]=arr[j];
backElment[temp]++;
}
//按照這個桶的順序(一維數組的下標一次取出數據,放入到原來的數組中)
int index=0;
//遍歷每一個桶,並將桶中的數據,放到原數組中
for(int i=0;i<backElment.length;i++) {
//如果桶中有數據,則將桶中數據循環放入原數組中
if(backElment[i]!=0) {
//循環該桶 即第I桶(即第i個一維數組) 放入
for(int k=0;k<backElment[i];k++) {
arr[index++]= msg[i][k];
}
}
//取出數組後將該桶置爲0
backElment[i]=0;
}
System.out.println("第一輪排序後的結果:===>"+Arrays.toString(arr));
for(int j=0;j<arr.length;j++) {
//取出個位數據
int temp=arr[j]/10 % 10;
//放到對應的桶中
msg[temp][backElment[temp]]=arr[j];
backElment[temp]++;
}
//按照這個桶的順序(一維數組的下標一次取出數據,放入到原來的數組中)
index=0;
//遍歷每一個桶,並將桶中的數據,放到原數組中
for(int i=0;i<backElment.length;i++) {
//如果桶中有數據,則將桶中數據循環放入原數組中
if(backElment[i]!=0) {
//循環該桶 即第I桶(即第i個一維數組) 放入
for(int k=0;k<backElment[i];k++) {
arr[index++]= msg[i][k];
}
}
backElment[i]=0;
}
System.out.println("第二輪排序後的結果:===>"+Arrays.toString(arr));
for(int j=0;j<arr.length;j++) {
//取出個位數據
int temp=arr[j]/100 % 10;
//放到對應的桶中
msg[temp][backElment[temp]]=arr[j];
backElment[temp]++;
}
//按照這個桶的順序(一維數組的下標一次取出數據,放入到原來的數組中)
index=0;
//遍歷每一個桶,並將桶中的數據,放到原數組中
for(int i=0;i<backElment.length;i++) {
//如果桶中有數據,則將桶中數據循環放入原數組中
if(backElment[i]!=0) {
//循環該桶 即第I桶(即第i個一維數組) 放入
for(int k=0;k<backElment[i];k++) {
arr[index++]= msg[i][k];
}
}
}
System.out.println("第三輪排序後的結果:===>"+Arrays.toString(arr));
}
最終代碼
/**
* 優化後的桶排序算法
* @param arr
*/
public static void radixSort2(int[] arr) {
// 桶排序的循環次數是根據數組中最大位數的來定義
// 1 查找到數組中最大的數據 獲取位數 我們可以吧第一位定義爲最大的數字信息
int maxValue = arr[0];
for (int j = 1; j < arr.length; j++) {
if (maxValue < arr[j]) {
maxValue = arr[j];
}
}
// 得到循環的次數
int arrCount = (maxValue + "").length();
// 首先需要定義桶的數量
int[][] msg = new int[10][arr.length];
// 用來記錄每個桶裝了幾個數據
int[] backElment = new int[10];
for (int i = 0, j = 1; i < arrCount; i++, j *= 10) {
for (int k = 0; k < arr.length; k++) {
// 取出個位數據
int temp = arr[k] / j % 10;
// 放到對應的桶中
msg[temp][backElment[temp]] = arr[k];
backElment[temp]++;
}
// 按照這個桶的順序(一維數組的下標一次取出數據,放入到原來的數組中)
int index = 0;
// 遍歷每一個桶,並將桶中的數據,放到原數組中
for (int l = 0; l < backElment.length; l++) {
// 如果桶中有數據,則將桶中數據循環放入原數組中
if (backElment[l] != 0) {
// 循環該桶 即第I桶(即第i個一維數組) 放入
for (int k = 0; k < backElment[l]; k++) {
arr[index++] = msg[l][k];
}
}
// 取出數組後將該桶置爲0
backElment[l] = 0;
}
//System.out.println("第"+(i+1)+"輪排序後的結果:===>"+Arrays.toString(arr));
}
}
4 測試性能
public static void main(String[] args) {
int [] arr= {54,16,32,3,256,724,1024};
radixSort(arr);
System.out.println("--------------");
//我們用數據來測試一下基數排序(桶排序)的性能
int []data=new int[8000000];
for(int i=0;i<8000000;i++) {
data[i]=(int) (Math.random()*80000);
}
SimpleDateFormat format=new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
Date str01=new Date();
System.out.println("排序前--------------"+format.format(str01));
radixSort2(data);
Date str02=new Date();
System.out.println("排序後--------------"+format.format(str02));
}
由於筆記本性能較差 ,測試8000000條數據只需要1秒鐘即可完成。當然機器性能越好,測試數據條數也可更多。
以上如有不妥,歡迎指出。