1. 程式人生 > >演算法 排序演算法之基數排序

演算法 排序演算法之基數排序

基數排序

基數排序也稱作桶排序,是一種當關鍵字為整數型別時非常高效的排序方法。

基本思想

設待排序的元素是m位d進位制整(不足m位的關鍵字在高位補0),設定d個桶,令其編號分別為0,1,2…d-1。

這裡寫圖片描述

這裡寫圖片描述

首先元素最低位的數值一次把各個元素放到相應的桶中

這裡寫圖片描述

然後按照桶號從小到大和進入桶中資料元素的先後次序收集分配在各個桶中的資料元素。

這裡寫圖片描述

這樣就形成了資料元素集合的一個新的排列,稱這樣的一次排序過程為一次基數排序。

再對一次基數排序得到的資料元素按關鍵字次低位的數值依次把各個資料元素放到相應的桶中

這裡寫圖片描述

按照桶號從小到大和進入桶中資料元素的先後次序收集分配在各桶中的資料元素

這裡寫圖片描述

這樣的過程重複進行

這裡寫圖片描述

這裡寫圖片描述

當完成了m次基數排序後,就得到了排好序的資料元素序列

程式碼實現

因為要求進出桶中的資料元素序列滿足先進先出的原則,因此這裡所說的桶實際就是佇列

佇列有順序佇列和鏈式佇列,因此在實現基數排序演算法時,有基於順序佇列和鏈式佇列兩種不同的實現方式

C語言實現

#include<math.h>
testBS()
{
    inta[] = {2, 343, 342, 1, 123, 43, 4343, 433, 687, 654, 3};
    int *a_p = a;
    //計算陣列長度
    intsize = sizeof(a) / sizeof
(int); //基數排序 bucketSort3(a_p, size); //列印排序後結果 inti; for(i = 0; i < size; i++) { printf("%d\n", a[i]); } intt; scanf("%d", t); } //基數排序 voidbucketSort3(int *p, intn) { //獲取陣列中的最大數 intmaxNum = findMaxNum(p, n); //獲取最大數的位數,次數也是再分配的次數。 intloopTimes = getLoopTimes(maxNum); inti; //對每一位進行桶分配
for(i = 1; i <= loopTimes; i++) { sort2(p, n, i); } } //獲取數字的位數 intgetLoopTimes(intnum) { intcount = 1; inttemp = num / 10; while(temp != 0) { count++; temp = temp / 10; } returncount; } //查詢陣列中的最大數 intfindMaxNum(int *p, intn) { inti; intmax = 0; for(i = 0; i < n; i++) { if(*(p + i) > max) { max = *(p + i); } } returnmax; } //將數字分配到各自的桶中,然後按照桶的順序輸出排序結果 voidsort2(int *p, intn, intloop) { //建立一組桶此處的20是預設的根據實際數情況修改 intbuckets[10][20] = {}; //求桶的index的除數 //如798個位桶index=(798/1)%10=8 //十位桶index=(798/10)%10=9 //百位桶index=(798/100)%10=7 //tempNum為上式中的1、10、100 inttempNum = (int)pow(10, loop - 1); inti, j; for(i = 0; i < n; i++) { introw_index = (*(p + i) / tempNum) % 10; for(j = 0; j < 20; j++) { if(buckets[row_index][j] == NULL) { buckets[row_index][j] = *(p + i); break; } } } //將桶中的數,倒回到原有陣列中 intk = 0; for(i = 0; i < 10; i++) { for(j = 0; j < 20; j++) { if(buckets[i][j] != NULL) { *(p + k) = buckets[i][j]; buckets[i][j] = NULL; k++; } } } }

java實現

 /**
     * 基數排序
     * 時間複雜度O(mn)
     * 空間複雜度O(mn)
     * 穩定的排序演算法
     *
     * @param arr 排序陣列
     */
    public static void radixSort(int[] arr) {
        int k = 0; //原陣列下標計數器
        int n = 1; //除數
        int digitIndex = 1;//控制鍵值排序依據在哪一位
        int maxDigit = getMaxDigit(arr);//該陣列的最大位數
        int[][] bucket = new int[10][arr.length];//陣列第一維表示可能的餘數為0-9
        int[] order = new int[10];//order[i]表示改位是i的數的個數

        while (digitIndex <= maxDigit) {
            for (int i = 0; i < arr.length; i++) {
                int lsd = ((arr[i] / n) % 10);
                bucket[lsd][order[lsd]] = arr[i];//按照鍵值把數放置在桶內
                order[lsd]++;
            }
            for (int i = 0; i < 10; i++) {
                if (order[i] != 0) {
                    for (int j = 0; j < order[i]; j++) {//重新收集桶內的序列
                        arr[k] = bucket[i][j];
                        k++;
                    }
                }
                order[i] = 0;//清空
            }
            n *= 10;//接下來按照下一位進行放置
            k = 0;
            digitIndex++;
        }
    }

    /**
     * 獲取整型陣列中資料元素的最大位數
     *
     * @param arr 整型陣列
     * @return 最大位數
     */
    private static int getMaxDigit(int[] arr) {
        int max = getDigit(arr[0]);
        for (int i = 1; i < arr.length; i++) {
            if (getDigit(arr[i]) > max) {
                max = getDigit(arr[i]);
            }
        }
        return max;
    }

    /**
     * 獲取一個整數的位數
     *
     * @param i 整數
     * @return 位數
     */
    private static int getDigit(int i) {
        return String.valueOf(i).length();
    }

演算法分析

基數排序演算法要進行m次迴圈,每次迴圈中先要把n個數據元素放到相應的d個佇列中,然後把各個佇列中的資料元素收回,鏈式佇列的插入演算法和刪除演算法的時間複雜度均是O(1),因此基於鏈式佇列的基數排序演算法的時間複雜度為O(2mn)或者簡寫成O(mn)

基於鏈式佇列的基數排序演算法要m次使用n個節點存放n個數據元素,因此空間複雜度是O(n)

從演算法的思想來看,基數排序演算法是一種穩定的排序演算法