數據結構與算法系列——排序(11)_基數排列 基數排序 基數排序詳解以及java實現 數據結構Java版之基數排序(四)

1. 工做原理(定義)

  基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不一樣的數字,而後按每一個位數分別比較。因爲整數也能夠表達字符串(好比名字或日期)和特定格式的浮點數,因此基數排序也不是隻能使用於整數。javascript

  基數排序(radix sort)屬於「分配式排序」(distribution sort),又稱「桶子法」(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些「桶」中,藉以達到排序的做用,基數排序法是屬於穩定性的排序,其時間複雜度爲O (nlog(r)m),其中r爲所採起的基數,而m爲堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。php

2. 算法步驟 

   最高位優先(Most Significant Digit first)法,簡稱MSD法:先按k 1排序分組,同一組中記錄,關鍵碼k 1相等,再對各組按k 2排序分紅子組,以後,對後面的關鍵碼繼續這樣的排序分組,直到按最次位關鍵碼k d對各子組排序後。再將各組鏈接起來,便獲得一個有序序列。
   最低位優先(Least Significant Digit first)法,簡稱LSD法:先從k d開始排序,再對k d-1進行排序,依次重複,直到對k 1排序後便獲得一個有序序列。
   LSD的基數排序適用於位數小的數列,若是位數多的話,使用MSD的效率會比較好。
  1. 將全部待比較數值(正整數)統一爲一樣的數位長度(最大元素長度),數位較短的數前面補零。html

  2. 從最低位開始,依次進行一次排序。 java

  3. 重複操做,直至完成最高位的排序。mysql

  

3. 動畫演示

  

4. 性能分析

1. 時間複雜度

 時間複雜度爲:k* length;其中 k爲數組元素最高位數,length爲元素個數;故時間複雜度爲O(K*N)。git

 在基數排序中,由於沒有比較操做,因此在複雜上,最好的狀況與最壞的狀況在時間上是一致的。所以不管是最好,最壞仍是平均複雜度都是O(d * (n + r))。其中,d 爲位數,r 爲基數,n 爲原數組個數。 算法

 設待排序列爲n個記錄,d個關鍵碼,關鍵碼的取值範圍爲radix,則進行鏈式基數排序的時間複雜度爲O(d(n+radix)),其中,一趟分配時間複雜度爲O(n),一趟收集時間複雜度爲O(radix),共進行d趟分配和收集。 空間效率:須要2*radix個指向隊列的輔助空間,以及用於靜態鏈表的n個指針。sql

2. 空間複雜度

  空間複雜度O(n+dr)數組

3. 算法穩定性 

 穩定的算法。數據結構

4. 初始順序狀態

  1. 比較次數:
  2. 移動次數:
  3. 複雜度:    
  4. 排序趟數:

5. 歸位

  不能歸位

6. 優勢

7. 具體代碼

普通正整數

import java.util.ArrayList;
import java.util.List;

public class RadixSort {
    
    /**
     * MSD方法1--兩個數組,基於計數排序
     * @param arr
     */
    public static void radixSortMSD(int[] arr) {
            int exp; // 指數。當對數組按各位進行排序時,exp=1;按十位進行排序時,exp=10;...
            int max = getMax(arr); //最大值
            int maxDigit = maxDigit(max);//最大數位
            for (exp = (int) Math.pow(10, maxDigit-1); exp >1 ; exp /= 10){//高位->低位
                countingSort(arr, exp);
            }
                
        }
    /**
     * LSD 方法1--兩個數組,基於計數排序
     * @param arr
     */
    public static void radixSort(int[] arr) {
        int exp;    // 指數。當對數組按各位進行排序時,exp=1;按十位進行排序時,exp=10;...
        int max = getMax(arr);// 數組arr中的最大值
        for (exp = 1; max/exp > 0; exp *= 10){//低位->高位
            countingSort(arr, exp);
        }
            
    }
    public static void countingSort(int[]arr, int exp){
        int[] output = new int[arr.length];    // 存儲"被排序數據"的臨時數組
        int[] buckets = new int[10];

        // 將數據出現的次數存儲在buckets[]中
        for (int i = 0; i < arr.length; i++){
            buckets[ (arr[i]/exp)%10 ]++;
        }
        
        // 更改buckets[i]。目的是讓更改後的buckets[i]的值,是該數據在output[]中的位置。
        for (int i = 1; i < 10; i++)
            buckets[i] += buckets[i - 1];

        // 將數據存儲到臨時數組output[]中
        for (int i = arr.length - 1; i >= 0; i--) {
            output[buckets[ (arr[i]/exp)%10 ] - 1] = arr[i];
            buckets[ (arr[i]/exp)%10 ]--;
        }

        // 將排序好的數據賦值給a[]
        for (int i = 0; i < arr.length; i++){
             arr[i] = output[i];
        }
    }
    /**
     * LSD 方法2--二維數組
     * @param arr
     */
    public static void radixSort2(int[] arr) {
            int maxDigit = maxDigit(getMax(arr));
             int digitValue=1;//表明位數對應的數:1,10,100...
            int RADIX = 10;
            int length=arr.length;
            int[][] bucket=new int[RADIX][length];//排序桶用於保存每次排序後的結果,這一位上排序結果相同的數字放在同一個桶裏
            int[] order=new int[RADIX];//用於保存每一個桶裏有多少個數字
            while(digitValue < Math.pow(RADIX, maxDigit+1)) {
                for(int num:arr) { //將數組arr裏的每一個數字放在相應的桶裏
                    int digit=(num/digitValue)%RADIX;
                    bucket[digit][order[digit]]=num;
                    order[digit]++;
                }
                int k=0;//保存每一位排序後的結果用於下一位的排序輸入
                for(int i=0;i<RADIX;i++) {//將前一個循環生成的桶裏的數據覆蓋到原數組中用於保存這一位的排序結果
                    if(order[i]!=0) {//這個桶裏有數據,從上到下遍歷這個桶並將數據保存到原數組中
                        for(int j=0;j<order[i];j++) {
                            arr[k++]=bucket[i][j];
                        }
                    }
                    order[i]=0;//將桶裏計數器置0,用於下一次位排序
                }
                digitValue*=RADIX;
            }
    }
    
    /**
     * LSD 方法3--數組列表
     * @param arr
     */
    public static void radixSort3(int[] arr) {
        int maxDigit = maxDigit(getMax(arr));
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        for(int i  = 0; i < 10; i ++) {
            list.add(new ArrayList<Integer>());
        }
        for(int i = 0, factor = 1; i < maxDigit; factor *= 10, i ++) {
            for(int j = 0; j < arr.length; j ++) {
                list.get((arr[j]/factor)%10).add(arr[j]);
            }
            for(int j = 0, k = 0; j < list.size(); j ++) {
                while(!list.get(j).isEmpty()) {
                    arr[k] = list.get(j).get(0);
                    list.get(j).remove(0);
                    k ++;
                }
            }
        }
    }
    /**
     * LSD 方法4--隊列
     * @param arr
     */
    public static void radixSort4(int[] arr) {
        //求最大位數
        int count = maxDigit(getMax(arr));
        int len = arr.length;
        //十個隊列,分別存儲數位數值爲0-9的元素
        BucketQueue [] queues = new BucketQueue[10];
        //各隊列初始化
        for(int i = 0; i < 10; i++) {
            queues[i] = new BucketQueue();
            queues[i].data = new int[len];
            queues[i].front = queues[i].rear = -1;
        }
        int m = 1;//m控制取第幾位(從個位開始取直到count)
        while(count > 0) {
            for(int i = 0; i < len; i++) {
                int t = arr[i] / m % 10;
                //根據數值分配入隊
                queues[t].data[++queues[t].rear] = arr[i];
            }
            //從隊號0-9順序出隊收集元素
            int s = 0;
            for(int j = 0; j < 10; j++) {
                while(queues[j].front != queues[j].rear) {
                    arr[s++] = queues[j].data[++queues[j].front];
                }
                //收集後隊列清空,方便下一趟排序
                queues[j].front = queues[j].rear = -1;
            }
            m *= 10;
            count--;
        }
    }
    /**
     * 獲取數組arr中最大值
     */
    private static int getMax(int[] arr) {
        int max;
        max = arr[0];
        for (int i = 1; i < arr.length; i++){
            if (arr[i] > max){
                 max = arr[i];
            }
        }
        return max;
    }
    
    //計算數組裏元素的最大位數
    private static int maxDigit(int max) {
        if (max == 0) {
            return 1;
        }
        int lenght = 0;
        for (int temp = max; temp != 0; temp /= 10) {
            lenght++;
        }
        return lenght;
    }
    
    public static void main(String[] args) {
        int[] A=new int[]{73,22, 93, 43, 55, 14, 28, 65, 39, 81};
        radixSortMSD(A);
        for(int num:A) {
            System.out.println(num);
        }
    }
}
class BucketQueue {
    int data[];
    int front;
    int rear;
}

字符串

 

public class RadixSort {

    public static void main(String[] args) {
        String[] words = {"Java", "Mongodb", "Redis", "Kafka", "javascript", "mysql", "mybatis", "kindle", "rpc", "Algorithm", "mergeSort", "quickSort", "Adobe"};
        radixSort(words);
        for (String word : words) {
            System.out.println(word.replaceAll("0", ""));
        }
    }
    
    //基數排序(單詞
    private static void radixSort(String[] words){
        int exp = 0;
        int maxLength = getMaxLength(words);
        autoComplete(words, maxLength);
        for(exp = 1; exp <= maxLength; exp++){
            countingSort(words, exp);
        }

    }

    //計數排序(單詞)
    private static void countingSort(String[] words, int exp){

        int n = words.length;
        String[] r = new String[n];
        int[] c = new int[122];
        for(int i = 0; i < n; ++i){
            int asc = (byte)words[i].charAt(words[i].length() - exp);
            c[asc]++;
        }

        for(int i = 1; i < 122; ++i){
            c[i] = c[i-1] + c[i];
        }

        for (int i = n - 1; i >= 0; --i){
            int asc = (byte)words[i].charAt(words[i].length() - exp);
            int index = c[asc];
            r[index - 1] = words[i];
            c[asc]--;
        }

        for(int i = 0; i < n; ++i){
            words[i] = r[i];
        }

    }

    //自動補全單詞
    private static void autoComplete(String[] words, int maxLength){
        int i = 0;
        for (String word : words) {
            if(word.length() < maxLength){
                int value = maxLength - word.length();
                StringBuilder sb = new StringBuilder();
                for(int j = 0; j < value; ++j){
                    sb.append("0");
                }
                words[i] = word + sb;
            }
            i++;
        }
    }

    //獲取字符串最大的長度
    private static int getMaxLength(String[] words){
        int maxLength = words[0].length();
        for(int i = 1; i < words.length; ++i){
            if(words[i].length() > maxLength)
                maxLength = words[i].length();
        }
        return maxLength;
    }

}

8. 摘錄網址

  1. 排序算法系列之基數排序
  2. 基數排序
  3. https://www.runoob.com/w3cnote/radix-sort.html
  4. https://visualgo.net/en/sorting
  5. 基數排序詳解以及java實現
  6. 數據結構Java版之基數排序(四)
  7. Java實現基數排序(RadixSort)的代碼示例
相關文章
相關標籤/搜索