Java常用的八種排序演算法與程式碼實現(三):桶排序、計數排序、基數排序
三種線性排序演算法:桶排序、計數排序、基數排序
線性排序演算法(Linear Sort):這些排序演算法的時間複雜度是線性的O(n),是非比較的排序演算法
桶排序(Bucket Sort)
將要排序的資料分到幾個有序的桶裡,每個桶裡的資料再單獨進行排序,桶內排完序之後,再把桶裡的資料按照順序取出,組成的序列就是有序的了
桶排序比較適合用在外部排序中,比如磁碟中的資料需要 排序而記憶體有限,沒有辦法將資料全部載入的情況
桶排序步驟:
1.找出待排序陣列中的最大值max、最小值min
2. 動態陣列ArrayList 作為桶,桶裡放的元素也用 ArrayList 儲存。桶的數量為(max-min)/arr.length+1
3.遍歷陣列 arr,計算每個元素 arr[i] 放的桶
4.每個桶各自排序
5.遍歷桶陣列,把排序好的元素放進輸出陣列
圖解:
程式碼:
/** * 桶排序 * * @param arr 待排陣列 */ public static void bucketSort(int[] arr) { int min = arr[0]; int max = arr[0]; // 獲取資料範圍 for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } if (arr[i] < min) { min = arr[i]; } } // 確定桶數 int count = (min + max) / arr.length + 1; ArrayList<ArrayList<Integer>> bucket = new ArrayList<>(count); for (int i = 0; i < count; i++) { bucket.add(new ArrayList<>()); } // 將元素放入桶 for (int i = 0; i < arr.length; i++) { int num = (arr[i] - min) / arr.length; bucket.get(num).add(arr[i]); } // 將桶中資料進行排序(排序已經完成) for (ArrayList<Integer> arrayList : bucket) { Collections.sort(arrayList); } // 將桶中資料匯入待排序的陣列中(只是為了顯示方便) int k = 0; for (int i = 0; i < bucket.size(); i++) { for (int j = 0; j < bucket.get(i).size(); j++) { arr[k++] = bucket.get(i).get(j); } } }
計數排序(Counting Sort)
桶排序的一種特殊情況,當要排序的n個數據,所處的範圍並不大的時候,比如最大值是k,我們就可以把資料劃分成k個桶,每個桶的表資料值都是相同的,省掉了桶內排序的步驟,在顯示生活中也存在這樣的案例,比如高考省榜,就是按照這種排序方式進行排列的(主要適用於資料比較集中的情況)
計數排序步驟:
1 求出待排序列的最大值和最小值
2 設定一個計數陣列,用來記錄每個元素出現的次數
3 記錄待排序列在計數陣列中的位置和次數
4 將計數陣列中的資料灌入待排序列中
圖解:
計數排序在生活中的應用:—— 高考省榜
程式碼:
/** * 計數排序 * * @param arr 待排陣列 */ public static void countingSort(int[] arr) { int min = arr[0]; int max = arr[0]; // 獲取資料範圍 for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } if (arr[i] < min) { min = arr[i]; } } // 建立計數陣列count,用來記錄資料出現的次數 int[] count = new int[max - min + 1]; for (int i = 0; i < arr.length; i++) { count[arr[i] - min]++; } // 將計數陣列中的資料灌入待排序陣列 int k = 0; for (int i = 0; i < count.length; i++) { if (count[i] != 0) { for (int j = 0; j < count[i]; j++) { arr[k++] = i + min; } } } }
基數排序(Radix Sort)
基數排序對要排序的資料有要求,需要可以分割出獨立的”位”進行比較,而且位與位之間有遞進關係,如果a資料的高位比b資料大,那剩下的地位就不用比較了,除此之外每一位的資料範圍不能太大,要可以用線性排序演算法來排序,否則基數排序的時間複雜度就無法做到O(n)
基數排序的步驟:
1 找出待排序列的未排高位
2 用線性排序按照未排高位進行線性排序,然後重複1過程,直至序列全部有序
圖解:
程式碼:
/**
* 基數排序
*
* @param arr 待排陣列
* @param len 待排位數
*/
public static void radixSort(int[] arr, int len) {
// 除數,從8位0開始,先比較高位
int divisor = (int) Math.pow(10, len);
// 確定桶數,桶的數量可設為10,因為位數的取值為0-9,並初始化桶
ArrayList<ArrayList<Integer>> bucket = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
bucket.add(new ArrayList<>());
}
for (int i = 0; i < arr.length; i++) {
// 只取個位
int c = (arr[i] / divisor) % 10;
bucket.get(c).add(arr[i]);
}
// 將桶中資料進行排序(排序已經完成)
for (ArrayList<Integer> arrayList : bucket) {
Collections.sort(arrayList);
}
// 將桶中資料匯入待排序的陣列中
int k = 0;
for (int i = 0; i < bucket.size(); i++) {
for (int j = 0; j < bucket.get(i).size(); j++) {
arr[k++] = bucket.get(i).get(j);
}
}
}
測試類及生成隨機陣列,隨機訂單的方法(基數排序的資料是50個8位訂單號)
/**
* 生成一個長度5-10的隨機陣列
*
* @return 隨機陣列
*/
private static int[] initArray() {
int len = 5 + new Random().nextInt(6);
int[] arr = new int[len];
for (int i = 0; i < arr.length; i++) {
arr[i] = new Random().nextInt(100);
}
return arr;
}
/**
* 為基數排序法準備的8位隨機訂單,50組資料
*
* @return 隨機訂單陣列
*/
private static int[] initArrayForRadixSort() {
String str = "";
int[] arr = new int[50];
Random random = new Random();
for (int i = 0; i < arr.length; i++) {
// 保證訂單首位數不為0,所以隨機0-9生成後7位,隨機1-9生成第一位
for (int j = 0; j < 7; j++) {
str += random.nextInt(10);
}
str = 1 + random.nextInt(9) + str;
arr[i] = Integer.valueOf(str);
str = "";
}
return arr;
}
public static void main(String[] args) {
// 基數排序
int[] arr = initArrayForRadixSort();
System.out.println("排序之前的陣列是:" + Arrays.toString(arr));
int count = (arr[0] + "").length() - 1;
for (int i = count; i < arr.length; i++) {
countingSort(arr, i);
}
System.out.println("排序之後的陣列是:" + Arrays.toString(arr));
// 非基數排序
int[] arr2 = initArray();
System.out.println("排序之前的陣列是:" + Arrays.toString(arr2));
quockSort(arr2);
System.out.println("排序之後的陣列是:" + Arrays.toString(arr2));
}
測試結果,親測有效:
桶排序:
排序之前的陣列是:[31, 64, 46, 94, 29, 99, 82, 48, 84]
排序之後的陣列是:[29, 31, 46, 48, 64, 82, 84, 94, 99]
計數排序:
排序之前的陣列是:[11, 20, 56, 2, 47, 75, 66, 21, 3, 74]
排序之後的陣列是:[2, 3, 11, 20, 21, 47, 56, 66, 74, 75]
基數排序:
排序之前的陣列是:[54920007, 10744441, 24003637, 22072166, 12466348, 17322066, 54489715, 64847150, 48366735, 80287146, 71463179, 82992316, 74996232, 37427568, 45629716, 71177033, 81944062, 74519384, 91743758, 68246062, 23798432, 96894141, 69555229, 49967525, 52587116, 43508294, 47269398, 97352691, 82058781, 67895293, 11909501, 30869797, 47345049, 50743493, 25530908, 85030153, 16247011, 76036389, 10846207, 88061686, 17767234, 25997527, 98880748, 26860131, 98633987, 88621984, 89076873, 70419980, 95812196, 21624491]
排序之後的陣列是:[10744441, 10846207, 11909501, 12466348, 16247011, 17322066, 17767234, 21624491, 22072166, 23798432, 24003637, 25530908, 25997527, 26860131, 30869797, 37427568, 43508294, 45629716, 47269398, 47345049, 48366735, 49967525, 50743493, 52587116, 54489715, 54920007, 64847150, 67895293, 68246062, 69555229, 70419980, 71177033, 71463179, 74519384, 74996232, 76036389, 80287146, 81944062, 82058781, 82992316, 85030153, 88061686, 88621984, 89076873, 91743758, 95812196, 96894141, 97352691, 98633987, 98880748]
8種排序演算法程式碼:https://github.com/lijialin0903/sort.git