先介紹一下概念
計數排序和基數排序都是非比較排序,就是不用進行比較就能排序,相對於堆排序,快速排序,插入排序等都是比較排序,比較排序算法的最壞情況下屆都要做0(nlgn)次的比較,堆排序和合並排序都是漸近最有的比較排序算法,線性時間排序的時間複雜度都是O(n)。
計數排序的基本思想,假設n個輸入元素中的每一個都是介於0到k的整數,此處k爲某個整數。當k=O(n)時,計數排序運行時間是O(n),計數排序基本思想是對每一個輸入元素x,確定出小於x的元素個數,這樣可以把x直接放在最終輸出的位置上。例如,有15個元素小於x,那麼x就放在第18個輸出位置。當有元素相同時,放完後小於個數要減1。
一般有兩種方式:
1) 高位優先(MSD): 從高位到低位依次對序列排序
2)低位優先(LSD): 從低位到高位依次對序列排序
計算機一般採用低位優先法(人類一般使用高位優先),但是採用低位優先時要確保排序算法的穩定性。
基數排序藉助桶式排序,每次按第N位排序時,採用桶式排序。對於如何安排每次落入同一個桶中的數據有兩種安排方法:
1)順序存儲:每次使用桶式排序,放入r個桶中,,相同時增加計數。
2)鏈式存儲:每個桶通過一個靜態隊列來跟蹤。
現在有數組:2,10,78,189,354,8,756,390,356。第一次根據各位數將數組劃分爲10個隊列(當然其中的某些隊列可能不含有元素)
0:10,390
1:
2:2
3:
4:354
5:
6:756,356
7:
8:78,8
9:189
然後收集成序列:
10,390,2,354,756,356,78,8,189
在進行第二次分組:
0:2,8
1:10
2:
3:
4:
5:354,756,356
6:
7:78
8:189
9:390
第二次收集:
2,8,10,354,756,356,78,189,390
第三次分配:
0:2,8,10,78
1:189
2:
3:354,356,390
4:
5:
6:
7:756
8:
9:
最後得到序列:2,8,10,78,189,354,356,390,756。完成排序!
基數排序其實是利用多關鍵字先達到局部有序,再調整達到全局有序。
具體實現:
package sort;
import java.util.*;
public class RadixSort {
/**
* 基數排序算法思想:
* 先找出最大的數有幾位數,這樣就確定了要進行幾次排序,
* 然後建立10個數組進行存放第n位數爲0,1,2...9的數字,
* 存放完之後要進行收集10個數組中的數字到原來的數組中,
* 然後在進行排序
* 第i位數爲0,1,2...9的計算方法:array[j] % Math.pow(10, i+1) / Math.pow(10,i)
* 即第一位數直接除以10的餘數就行了,第二位數,則除以100得餘數,再除以10得商即爲第二位數,以此類推。
*
* @param array
* 要進行基數排序的數組
*/
public void radixSort(int[] array) {
int max, time;
// 確定最大的數有幾位數,確定排序的次數
max = array[0];
for (int i = array.length - 1; i > 0; i--) {
if (max < array[i]) {
max = array[i];
}
}
time = 0;
while (max > 0) {
max /= 10;
time++;
}
// 建立10個數組
ArrayList<Integer>[] quene = new ArrayList[10];
for (int i = 0; i < 10; i++) {
quene[i] = new ArrayList<Integer>();
}
// 分別進行time次分配和收集數組,根據不同的位數進行分配到數組中,再收集到原來的數組中
for (int i = 0; i < time; i++) {
for (int j = 0; j < array.length; j++) {
int index = (int) (array[j] % (int) Math.pow(10, i + 1) / Math
.pow(10, i));
quene[index].add(array[j]);
}
// 收集完了之後進行分配
int count = 0;
for (int k = 0; k < 10; k++) {
while (quene[k].size() > 0) {
array[count] = quene[k].remove(0);
count++;
}
}
}
}
/**
* 計數排序基本思想:
* 計算數組中每個元素x中比x小的元素個數,
* 然後就可以把x放在相應位置上
* 如:有15個數小於x,則x位於第16個位置上。
*
* @param data
* 待排序數組
* @param k
* 排序數組中最大的數
*/
public int[] countingSort(int[] data, int k) {
int result[] = new int[data.length];
int[] temp = new int[k + 1];
// 先初始化臨時數組,即保存小於元素x的個數的數組
for (int i = 0; i < temp.length; i++) {
temp[i] = 0;
}
// temp [i]即包含等於i的元素個數
for (int i = 0; i < data.length; i++) {
temp[data[i]] = temp[data[i]] + 1;
}
// temp [i]即包含小於等於i的元素個數
for (int i = 1; i < temp.length; i++) {
temp[i] = temp[i] + temp[i - 1];
}
// 將結果放入result數組,A[j]直接放在最終位置(小於等於A[j]的個數加1的位置)
// temp[data[j]]+1上,由於數組下標和位置相差1,下標從length開始是爲了保持穩定性,因爲同一個元素排序後,靠後的元素相對也是從後面存入數組的
for (int j = data.length - 1; j >= 0; j--) {
int index = temp[data[j]];
result[index - 1] = data[j];
temp[data[j]]--;
}
return result;
}
/**
* 得到數組中的最大數,用於計數排序
*
* @param data
* 待計算數組
* @return 返回最大數
*/
public int getMax(int[] data) {
int max = data[0];
for (int i = data.length - 1; i > 0; i--) {
if (max < data[i]) {
max = data[i];
}
}
return max;
}
public static void main(String[] args) {
RadixSort redix = new RadixSort();
int[] testData = { 2,10,78,189,354,8,756,390,356 };
// 基數排序測試
System.out.println("***基數排序測試***");
redix.radixSort(testData);
for (int data : testData) {
System.out.print(data + ",");
}
// 計數排序測試
System.out.println("\n***計數排序測試***");
int[] testData2 = { 9, 3, 5, 3, 6, 5, 3, 7, 4, 9 };
int max = redix.getMax(testData2);
int[] countSort = redix.countingSort(testData2, max);
for (int data : countSort) {
System.out.print(data + ",");
}
}
}
結果截圖如下: