數據結構與算法-學習筆記(九)

線性排序

桶排序、計數排序、基數排序這三種排序算法由於時間複雜度是O(n)線性的,所以把這類排序算法叫作線性排序。他們是非基於比較的排序算法。算法

這種排序算法對排序的數據要求很苛刻,主要掌握他們的適用場景。數組

桶排序

  1. 若是要排序的數據有n個,把n個數據均勻分到m個桶內,每一個桶內有k=n/m個元素。
  2. 每一個桶內的數據使用快排,時間複雜度O(klogk)。m個桶的總時間O(mklogk)。k=n/m =>O(nlog(n/m))。
  3. 當桶的個數m無限接近數據個數n時,log(n/m)就是個很是小的常量,此時時間複雜度=O(n);

以上,能夠看出實際使用桶排序時,須要數據在各個桶間分佈均勻,若是某些桶內數據很是多,某些又不多,則每一個桶內數據排序的時間複雜度就不是很是小的常量了。在極端狀況下,全部數據都進到一個桶內,O(nlogn);bash

適用狀況

外部排序:數據存儲在外部磁盤中,數據量大,內存有限,沒法將數據所有加載到內存。ui

此時使用桶排序,就能夠將數據按照桶的順序,一桶桶加載到內存進行排序。spa

計數排序

能夠當作一種特殊的桶排序,它先找到數據的最大值k(也就是數據的範圍),而後將數據劃分到k個桶內。則每一個桶內的數據值都是相等的,省了桶內排序的時間。code

可是具體計算,不是直接將數據存入桶內,而是將數據的個數放入桶內,再用計數的方式算出位置。cdn

// 計數排序,a 是數組,n 是數組大小。假設數組中存儲的都是非負整數。
public void countingSort(int[] a, int n) {
  if (n <= 1) return;

  // 查找數組中數據的範圍
  int max = a[0];
  for (int i = 1; i < n; ++i) {
    if (max < a[i]) {
      max = a[i];
    }
  }

  int[] c = new int[max + 1]; // 申請一個計數數組 c,下標大小 [0,max]
  for (int i = 0; i <= max; ++i) {
    c[i] = 0;
  }

  // 計算每一個元素的個數,放入 c 中
  for (int i = 0; i < n; ++i) {
    c[a[i]]++;
  }

  // 依次累加
  for (int i = 1; i <= max; ++i) {
    c[i] = c[i-1] + c[i];
  }

  // 臨時數組 r,存儲排序以後的結果
  int[] r = new int[n];
  // 計算排序的關鍵步驟,有點難理解
  for (int i = n - 1; i >= 0; --i) {
    int index = c[a[i]]-1;
    r[index] = a[i];
    c[a[i]]--;
  }

  // 將結果拷貝給 a 數組
  for (int i = 0; i < n; ++i) {
    a[i] = r[i];
  }
}
複製代碼

綜上,計數排序只能用在數據範圍不大的場景中,若是數據最大範圍K比數據量n大不少,就會有不少空桶,不適用。且計數排序只能給非負整數排序,若是數據是其餘類型,能夠在不改變相對大小的狀況下,轉化爲非負整數。blog

基數排序

相似對手機號碼這樣的數據排序,由於數據範圍大因此桶排序和計數都不行。排序

基數排序:按照每一位數據進行穩定排序。內存

每一位排序可使用桶排序或計數,則時間複雜度O(n)。排序數據有k爲=O(kn),若是k較小,則接近O(n);

若是不是等長的數據能夠補齊,如用‘0’這樣不影響原有大小順序的數據。

綜上:基數排序要求數據能夠分割出獨立的‘位’來比較,且位之間有遞進關係(如:個十百位)。同時,每一位的數據範圍不要太大,要可使用線性排序算法來排序。不然時間複雜度就達不到n了。

相關文章
相關標籤/搜索