極客算法訓練筆記(十),十大經典排序之計數排序、基數排序

非比較類排序

圖片十大經典排序算法江山圖圖片十大排序分類java

終於來到了最後兩個算法,非比較類的線性時間複雜度算法,計數排序和基數排序。上一篇也提到過,這幾種排序算法理解起來都不難,時間、空間複雜度分析起來也很簡單,可是對要排序的數據要求很苛刻,上一篇提到的桶排序就是適用於外部排序中,即所謂的外部排序就是數據存儲在外部磁盤中,數據量比較大,內存有限,沒法將數據所有加載到內存中。算法

計數排序實際上是桶排序的一種特殊狀況。當要排序的n個數據,所處的範圍並不大的時候,好比最大值是k,咱們就能夠把數據劃分紅k個桶。每一個桶內的數據值都是相同的,省掉了桶內排序的時間。數組

計數排序場景

例如高考查分系統,系統會顯示咱們的成績以及所在省的排名。若是你所在的省有50萬考生,如何經過成績快速排序得出名次呢?微信

考生的滿分是900分,最小是0分,這個數據的範圍很小,因此咱們能夠分紅901個桶,對應分數從0分到900分。根據考生的成績,咱們將這50萬考生劃分到 這901個桶裏。桶內的數據都是分數相同的考生,因此並不須要再進行排序。咱們只須要依次掃描每一個桶,將桶內的考生依次輸出到一個數組中,就實現了50萬考生的排序。由於只涉及掃描遍歷操做,因此時間複雜度是O(n)。ide

計數排序的算法思想就是這麼簡單跟桶排序很是相似,只是桶的大小粒度不同url

基數排序場景

假設咱們有10萬個手機號碼,但願將這10萬個手機號碼從小到大排序,用什麼方法快速排序?spa

對於桶排序、計數排序,手機號碼有11位,範圍太大,顯然不適合用這兩 種排序算法。手機號碼有這樣的規律:假設要比較兩個手機號碼a,b的大小,若是在前面幾位中,a手機號碼已經比b手機號碼大了,那後面的幾位就不用看了。根據這個思路,先按照最後一位來排序手機號碼,而後,再按照倒數第二位從新排序,以此類推,最後按照第一位從新排序。通過11次排序以後,手機號碼就都有序了。code

這就是基數排序的算法思路,基數排序對要排序的數據是有要求的,須要能夠分割出獨立的「位」來比較,並且位之間有遞進的關係,若是「a」數據的高位比「b」數據大,那剩下的低 位就不用比較了。除此以外,每一位的數據範圍不能太大,要能夠用線性排序算法來排序,不然,基數排序的時間複雜度就沒法作到 O(n) 了。orm

計數排序

算法思想

就是把數組元素值做爲數組的下標,而後用一個臨時數組統計該元素出現的次數,例如 temp[i] = m, 表示元素 i 一共出現了 m 次。最後再把臨時數組統計的數據從小到大彙總起來,此時彙總起來是數據是有序的。blog

動圖演示

計數排序

由圖可知,計數排序須要開闢一個臨時數組來存儲,先遍歷原數組一個個放入,而後再遍歷臨時數組一個個取出。

代碼實現

public class CountSort {

    public static int[] countSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return arr;
        }

        int length = arr.length;
        int max = arr[0];
        int min = arr[0];
        // 數組的最大值與最小值
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] < min) {
                min = arr[i];
            }

            if(max < arr[i]) {
                max = arr[i];
            }

        }

        // 建立大小爲max的臨時數組
        int[] temp = new int[max + 1];
        // 統計元素i出現的次數
        for (int i = 0; i < length; i++) {
            temp[arr[i]]++;
        }

        // 把臨時數組統計好的數據彙總到原數組
        int k = 0;
        for (int i = 0; i <= max; i++) {
            for (int j = temp[i]; j > 0; j--) {
                arr[k++] = i;
            }
        }

        return arr;
    }


    public static void main(String[] args) {
        int[] arr = {21,4,12,42,46,23,27,11,6,5,33,29,41,46,40,13,31};
        arr = countSort(arr);
        System.out.print("數組排序以後:");
        for (int i=0; i<arr.length; i++) {
            System.out.print(arr[i] + ",");
        }

    }

}

時間複雜度分析

O(n+k)

空間複雜度分析

O(n+k)

穩定性分析

穩定

適用條件

計數排序只能用在數據範圍不大的場景中,若是數據範圍「k」比要排序的數據「n」大不少,就不適合用計數排序了。並且,計數排序只能給非負整數排序,若是要排序的數據是其餘類型的,要將其在不改變相對大小的狀況下,轉化爲非負整數。

好比,仍是拿考生這個例子。若是考生成績精確到小數後一位,咱們就須要將全部的分數都先乘以10,轉化成整數,而後再放到9010個桶內。再好比,若是要排序的數據中有負數,數據的範圍是[-1000, 1000],那咱們就須要先對每一個數據都加1000,轉化成非負整數,由於數組的下邊不多是負數。

基數排序

算法思想

分別以個,十,百...位上數字大小對數組進行排序,最後概括彙總獲得總體有序的數組。

動圖演示

圖片基數排序

這裏的數最多兩位數,少於兩位數的比較十位數的時候,能夠十位數補0比較:

第一遍是按照個位數排的,獲得的數組是個位數有序;

第二遍再按照十位數排,獲得的數組所有有序;

代碼實現

import java.util.ArrayList;

public class RadixSort {

    public static int[] radixSort(int[] arr) {
        if(arr == null || arr.length < 2return arr;

        int n = arr.length;
        int max = arr[0];
        // 最大值
        for (int i = 1; i < n; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }

        // 計算最大值是幾位數
        int num = 1;
        while (max / 10 > 0) {
            num++;
            max = max / 10;
        }

        // 建立10個桶
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>(10);
        //初始化桶
        for (int i = 0; i < 10; i++) {
            bucketList.add(new ArrayList<Integer>());
        }

        // 進行每一趟的排序,從個位數開始排
        for (int i = 1; i <= num; i++) {
            for (int j = 0; j < n; j++) {

                // 獲取每一個數最後第 i 位是數組
                int radio = (arr[j] / (int)Math.pow(10,i-1)) % 10;
                //放進對應的桶裏
                bucketList.get(radio).add(arr[j]);

            }

            //合併放回原數組
            int k = 0;
            for (int j = 0; j < 10; j++) {
                for (Integer t : bucketList.get(j)) {
                    arr[k++] = t;
                }
                //取出來合併了以後把桶清光數據
                bucketList.get(j).clear();
            }
        }
        return arr;
    }

    public static void main(String[] args) {
        int[] arr = {21,4,12,42,46,23,27,11,6,5,33,29,41,46,40,13,31};
        arr = radixSort(arr);
        System.out.print("數組排序以後:");
        for (int i=0; i<arr.length; i++) {
            System.out.print(arr[i] + ",");
        }

    }

}

時間複雜度分析

O(n*k),k表明桶的個數

空間複雜度分析

O(n+k),k表明桶的個數

穩定性分析

穩定。

適用條件

須要能夠分割出獨立的「位」來比較,並且位之間有遞進的關係,若是「a」數據的高位比「b」數據大,那剩下的低位就不用比較了。除此以外,每一位的數據範圍不能太大,要能夠用線性排序算法來排序,不然,基數排序的時間複雜度就沒法作到O(n)了。

歡迎點贊分享在看,關注公衆號《阿甘的碼路》看更多內容,有問題能夠加我微信私我指正,各大平臺也會同步只不過排版可能有些會不太同樣~

相關文章
相關標籤/搜索