一、基數排序(桶排序)介紹
來源360百科:
基數排序(radix sort)屬於"分配式排序"(distribution sort),又稱"桶子法"(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些"桶"中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度為O (nlog(r)m),其中r為所採取的基數,而m為堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。
從上面的簡單介紹,是並不瞭解基數排序是怎麼弄的~基數排序不同與其他的7種排序,其他7種排序本質上都是按照交換或者比較來進行排序,但是基數排序並不是,它是按照分配,回收(分配到不同的位置上,然後回收)..不斷分配..回收來進行排序,直到有序..
聽上去好像很高大上,很難的樣子,其實不然。基數排序挺簡單的,下面我就來看一下基數排序的流程....
我們有9個桶,將陣列的數字按照數值分配桶中:
ps:圖片來源於網路,侵刪
上面我們發現:如果將桶按順序進行回收,那麼我們的排序就完成了~
可是,一般我們的陣列元素都不僅僅是個位數的數字的呀,那麼高位數的數字又怎麼弄呢??比如:23,44,511,6234這些高位數..
其實也是一樣的:
- 第一趟桶排序將數字的個位數分配到桶子裡面去,然後回收起來,此時陣列元素的所有個位數都已經排好順序了
- 第二趟桶排序將數字的十位數分別分配到桶子裡面去,然後回收起來,此時陣列元素的所有個位數和十位數都已經排好順序了**(如果沒有十位數、則補0)**
- 第三趟桶排序將數字的百位數分別分配到桶子裡面去,然後回收起來,此時陣列元素的所有個位數和十位數和百位數都已經排好順序了**(如果沒有百位數、則補0)**
- ..................................
- 直至全部數(個、十、百、千位...)排好順序,那麼這個陣列就是有序的了。
ps:圖片來源於網路,侵刪
機智的同學可能就會發現了,關於這個桶我們可以用二維陣列來進行存放。
10個桶子就是10列,如果分配時有的數字相同的話,那麼就弄成多行~
二、基數排序體驗
首先我們有以下這個陣列:
int[] arrays = {6, 4322, 432, 344, 55 };
複製程式碼
現在我們有10個桶子,每個桶子下能裝載arrays.length
個數字..
int[][] buckets = new int[arrays.length][10];
複製程式碼
效果如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
2.1第一趟分配與回收
將陣列的每個個位數進行分配到不同的桶子上:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6 | |||||||||
4322 | |||||||||
432 | |||||||||
344 | |||||||||
55 |
分配完之後,我們按照順序來進行回收:得到的結果應該是這樣子的:{4322,432,344,55,6}
2.2第二趟分配與回收
將陣列的每個十位數進行分配到不同的桶子上(像6這樣的數,往前邊補0):
於是我們可以得到這樣的排序:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6 | |||||||||
4322 | |||||||||
432 | |||||||||
344 | |||||||||
55 |
分配完之後,我們按照順序來進行回收:得到的結果應該是這樣子的:{6,4322,432,344,55}
2.3第三趟分配與回收
將陣列的每個百位數進行分配到不同的桶子上(像6、55這樣的數,往前邊補0):
於是我們可以得到這樣的排序:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6 | |||||||||
55 | 4322 | ||||||||
432 | |||||||||
344 | |||||||||
分配完之後,我們按照順序來進行回收:得到的結果應該是這樣子的:{6,55,4322,344,432}
2.3第四趟分配與回收
將陣列的每個百位數進行分配到不同的桶子上(像6、55,344,432這樣的數,往前邊補0):
於是我們可以得到這樣的排序:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6 | |||||||||
55 | |||||||||
344 | |||||||||
432 | |||||||||
4322 |
分配完之後,我們按照順序來進行回收:得到的結果應該是這樣子的:{6,55,344,432,4322}
此時我們的陣列就已經可以排好序了~~~過程就是這樣子,其實不難就只有兩個步驟:
- 將陣列的每一位放進桶子裡
- 回收
- 迴圈......
三、基數排序程式碼編寫
我們的基數排序是按照個、十、百、千位...來進行存放的。前面的演示是已經知道陣列元素的資料的情況下來進行存放,但是一般我們是不去理會陣列內元素的值的。那如果位數很多(萬位)或者都是個位數,這個條件我們怎麼去處理呢?
我們可以這樣做:先求出陣列最大的值,然後不斷/10,只要它能大於0,那麼它的位數還有~:
3.1求出陣列最大的值
這個我在前面寫遞迴的時候就有這個程式碼了,我就直接搬去遞迴的程式碼過來了,順便複習一哈吧:
- 當然了,更好的是直接用for迴圈來找出來就行了(易讀性好一些)
/**
* 遞迴,找出陣列最大的值
* @param arrays 陣列
* @param L 左邊界,第一個數
* @param R 右邊界,陣列的長度
* @return
*/
public static int findMax(int[] arrays, int L, int R) {
//如果該陣列只有一個數,那麼最大的就是該陣列第一個值了
if (L == R) {
return arrays[L];
} else {
int a = arrays[L];
int b = findMax(arrays, L + 1, R);//找出整體的最大值
if (a > b) {
return a;
} else {
return b;
}
}
複製程式碼
3.2程式碼實現
public static void radixSort(int[] arrays) {
int max = findMax(arrays, 0, arrays.length - 1);
//需要遍歷的次數由陣列最大值的位數來決定
for (int i = 1; max / i > 0; i = i * 10) {
int[][] buckets = new int[arrays.length][10];
//獲取每一位數字(個、十、百、千位...分配到桶子裡)
for (int j = 0; j < arrays.length; j++) {
int num = (arrays[j] / i) % 10;
//將其放入桶子裡
buckets[j][num] = arrays[j];
}
//回收桶子裡的元素
int k = 0;
//有10個桶子
for (int j = 0; j < 10; j++) {
//對每個桶子裡的元素進行回收
for (int l = 0; l < arrays.length ; l++) {
//如果桶子裡面有元素就回收(資料初始化會為0)
if (buckets[l][j] != 0) {
arrays[k++] = buckets[l][j];
}
}
}
}
}
複製程式碼
搞了一堆數測試了一哈:
四、總結
基數排序(桶排序)要理解起來並不困難,不過值得注意的是:基數排序對有負數和0的數列難以進行排序
- 因此,往往有0和負數的陣列一般我們都不用基數來進行排序
基數排序的要點就兩個:
- 分配:按照元素的大小來放入不同的桶子裡
- 回收:將桶子裡的元素按桶子順序重新放到陣列中
- 重複.....兩個步驟
參考資料:
- www.cnblogs.com/skywang1234…
- www.cnblogs.com/developerY/…
- www.cnblogs.com/zengzhihua/…
- www.cnblogs.com/jin-nuo/p/5…
- www.cnblogs.com/protected/p…
- www.cnblogs.com/Braveliu/ar…
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y