資料結構之計數排序

麥田裡的守望者_zhg發表於2020-10-06

1 計數排序

計數排序是一種比較快速的排序方法,相對於氣泡排序快速排序堆排序雞尾酒排序等,計數排序是一種不需要進行元素之間對比的排序演算法,但是該演算法也有一定的侷限性。
演算法思路:
需要使用一個計數陣列,將待排序陣列中的每個元素遍歷一遍,將每個元素出現的個數放到計數陣列中,元素的值是多少就在計數陣列相應的下標元素上的計數加1。舉例:

待排序陣列:[2,4,3,2,2,2,5,5,6,2,9,8,8,9]
通過計算,可以知道當前陣列的最大值是9,所以建立一個大小為9+1的陣列,然後遍歷該陣列,並記錄每個元素出現的次數,計數陣列如下:
[0, 0, 5, 1, 1, 2, 1, 0, 2, 2]
然後,按順序遍歷該計數陣列即可,陣列中元素的值代表以該下標為值的元素在陣列中出現的次數。結果如下:
升序排序:[2, 2, 2, 2, 2, 3, 4, 5, 5, 6, 8, 8, 8, 9]

2 樸素版計數排序程式碼

public class CountSort {
    private int[] array;

    public CountSort(int[] array) {
        this.array = array;
    }

    public void increaseSortOrigin(){
        int n = this.array.length;
        int max = this.array[0];
        for (int i = 1; i < n; i++) {
            if (this.array[i]>max)
                max=this.array[i];
        }
        int [] countArray = new int[max+1];
        for (int i = 0; i < n; i++) {
            countArray[this.array[i]] ++;
        }
        System.out.println(Arrays.toString(countArray));
        int c=0;
        for (int i = 0; i < max; i++) {
            for (int j = 0; j < countArray[i]; j++) {
                this.array[c] = i;
                c++;
            }
        }
    }
}

3 優化版計數排序

當待排序陣列中的最大值很大時,那麼可能會浪費很多空間,因為需要建立一個空間很大的計數陣列來存放計數,且此時元素有不多的話,那浪費將更加嚴重。那麼如何對其進行優化呢,可以使用一個最大和最小值,使用相對值來存放計數。
在這裡插入圖片描述

如下面的陣列:
[92,94,93,92,92,92,95,95,96,92,99,98,98,99],如果按照上面的思路可能需要一個陣列空間為99的計數陣列,但是元素的值主要集中在90-100之間,那麼前面90個空間就浪費了。所以使用相對位置進行計數。讓陣列中每一個元素減去陣列中的最小元素的值,當對計數陣列進行還原時,再加上最小值進行還原。使用相對計數的話,可以看到計數陣列的空間就小很多。
在這裡插入圖片描述

public class CountSort {
    private int[] array;

    public CountSort(int[] array) {
        this.array = array;
    }

    public void increaseSort(){
        int n = this.array.length;
        int max = this.array[0];
        int min = this.array[0];
        for (int i = 1; i < n; i++) {
            if (this.array[i] > max)
                max = this.array[i];
            if (this.array[i] < min)
                min= this.array[i];
        }
        int [] countArray = new int[max-min+1];
        for (int i = 0; i < n; i++) {
            countArray[this.array[i]-min] ++;
        }
        System.out.println(Arrays.toString(countArray));
        int c = 0;
        for (int i = 0; i < max-min+1; i++) {
            for (int j = 0; j < countArray[i]; j++) {
                this.array[c] = i+min;
                c++;
            }
        }
    }
}

4 演算法複雜度分析

4.1 時間複雜度

由於計數排序使用使用一個計數陣列進行緩衝,且計數陣列的大小為max-min=m,原始陣列的大小為n,從演算法中可以看到,需要對原陣列資料進行3次遍歷,對計數資料進行1次遍歷,所以空間複雜度為3n+m->O(n+m)

4.2 空間複雜度

如果不考慮原陣列的大小的話,只考慮額外的開銷,那麼該演算法的空間複雜度為O(m)

相關文章