數據結構與算法系列——排序(12)_計數排序 數據結構與算法——計數排序、桶排序、基數排序

1. 工做原理(定義)

  計數排序是一個非基於比較的排序算法,該算法於1954年由 Harold H. Seward 提出。它的優點在於在對必定範圍內的整數排序時,它的複雜度爲Ο(n+k)(其中k是整數的範圍),快於任何比較排序算法。固然這是一種犧牲空間換取時間的作法,並且當O(k)>O(n*log(n))的時候其效率反而不如基於比較的排序(基於比較的排序的時間複雜度在理論上的下限是O(n*log(n)), 如歸併排序,堆排序)html

  計數排序是一種穩定的線性時間排序算法。計數排序使用一個額外的數組 C ,其中第i個元素是待排序數組 A中值等於 i的元素的個數。而後根據數組 C 來將 A中的元素排到正確的位置。 java

2. 算法步驟

(1)找出待排序的數組中最大和最小的元素,肯定數組大小C[max-min+1]。算法

(2)統計數組中每一個值爲i的元素出現的次數,存入數組C的第i項數組

(3)對全部的計數累加(從C中的第一個元素開始,每一項和前一項相加)數據結構

(4)反向填充目標數組:將每一個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1post

3. 動畫演示

4. 性能分析

1. 時間複雜度

  當輸入的元素是 n 個 0 到 k 之間的整數時,它的運行時間是 Θ(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序算法。故時間複雜度爲O(N)。性能

2. 空間複雜度

  排序過程當中,須要一個輔助空間,所以空間複雜度爲O(n)。動畫

3. 算法穩定性 

  是穩定的算法。url

4. 初始順序狀態

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

5. 歸位

 

6. 優勢

  它的優點在於在對必定範圍內的整數排序時,它的複雜度爲Ο(n+k)(其中k是整數的範圍),快於任何比較排序算法。固然這是一種犧牲空間換取時間的作法,並且當O(k)>O(n*log(n))的時候其效率反而不如基於比較的排序(基於比較的排序的時間複雜度在理論上的下限是O(n*log(n)), 如歸併排序,堆排序)spa

7. 具體代碼

import java.util.Arrays;
public class CountingSort {
    public static void main(String[] args) {
        int[] a = {9, 7, 6, 3, 9, 2, 7, 3, 2, 8};
       countingSort(a);
        System.out.println(Arrays.toString(a));
    }
    //方法1,歸位的時候是反向
    public static void countingSort(int[] arr) {
        // 計算最大最小值,嚴謹實現最好用ifPresent檢查下
        int max = Arrays.stream(arr).max().getAsInt();
        int min = Arrays.stream(arr).min().getAsInt();
        int N = arr.length;
        int R = max - min + 1; // 最大最小元素之間範圍[min, max]的長度
        // 1. 計算頻率,在須要的數組長度上額外加1
        int[] count = new int[R];
        for (int i = 0; i < N; i++) {
            // 使用加1後的索引,有重複的該位置就自增
            count[arr[i] - min]++;
        }
        // 2. 頻率 -> 元素的開始索引
        for (int r = 1; r < R; r++) {
            count[r] += count[r-1];
        }

        // 3. 元素按照開始索引分類,用到一個和待排數組同樣大臨時數組存放數據
        int[] aux = new int[N];
        for (int i = N-1; i >=0; i--) {
            // 填充一個數據後,自增,以便相同的數據能夠填到下一個空位
            aux[count[arr[i] - min]-1] = arr[i];
            count[arr[i] - min]--;
        }
        // 4. 數據回寫
        for (int i = 0; i < N; i++) {
            arr[i] = aux[i];
        }
    }
    //方法2,歸位的時候是正向
    public static void countingSort2(int[] arr) {
        // 計算最大最小值,嚴謹實現最好用ifPresent檢查下
        int max = Arrays.stream(arr).max().getAsInt();
        int min = Arrays.stream(arr).min().getAsInt();
        int N = arr.length;
        int R = max - min + 1; // 最大最小元素之間範圍[min, max]的長度
        // 1. 計算頻率,在須要的數組長度上額外加1
        int[] count = new int[R+1];
        for (int i = 0; i < N; i++) {
            // 使用加1後的索引,有重複的該位置就自增
            count[arr[i] - min + 1]++;
        }
        // 2. 頻率 -> 元素的開始索引
        for (int r = 0; r < R; r++) {
            count[r + 1] += count[r];
        }

        // 3. 元素按照開始索引分類,用到一個和待排數組同樣大臨時數組存放數據
        int[] aux = new int[N];
        for (int i = 0; i < N; i++) {
            // 填充一個數據後,自增,以便相同的數據能夠填到下一個空位
            aux[count[arr[i] - min]++] = arr[i];
        }
        // 4. 數據回寫
        for (int i = 0; i < N; i++) {
            arr[i] = aux[i];
        }
    }
}

 

反向圖解釋

 

8. 參考網址

  1. https://www.runoob.com/w3cnote/counting-sort.html
  2. 排序算法系列之計數排序
  3. 數據結構與算法——計數排序、桶排序、基數排序
  4. https://visualgo.net/en/sorting
  5. https://www.sohu.com/a/258882297_478315
相關文章
相關標籤/搜索