1. 程式人生 > >資料結構 | 線性排序

資料結構 | 線性排序

資料結構與演算法之美學習總結,這一課講了三個線性排序,這三種排序時間複雜度都是 O ( n ) O(n)

目錄

1. 桶排序(Bucket sort)

n n 個數據分到 m m 個桶內,每個桶會有 k

= n / m k=n/m 個元素,每個桶內使用快速排序。桶排序的時間複雜度為 O ( m
k l o g k ) O(mklogk)
,如果桶的數量越接近 n n ,就會退變成 O ( n ) O(n)

桶排序對資料的要求很高:

  1. 桶與桶之間有天然大小順序;
  2. 資料在桶之間的分佈均勻。

桶排序適合用於外部排序: 資料存在外部磁碟中,資料量大而記憶體小,無法將資料全部載入到記憶體。

如果對 10 GB 的訂單按照金額排序,但是記憶體又不夠大,就可以對其進行桶排序。一般情況下 1 ~ 1000 的金額會比較多,那就對這部分再進行桶排序,直到記憶體可以裝得下。

2. 計數排序(Counting sort)

計數排序可以看做桶排序的特殊情況。 但是桶的大小粒度不一樣,一般來說,計數排序的桶會偏大但偏少。所以當資料的範圍 k 比要排序的資料 n 大得多,就不適合用計數排序。

計數排序只能給非負整數排序。 特殊情況的處理:

  1. 如果資料範圍是 [-1000, 1000],那麼我們給每一個數據加 1000,範圍就變為 [0, 2000];
  2. 如果資料精度是小數點後一位如,[0.1, 9.8],我們可以給每個資料乘以 10,就變為 [1, 98]。

計數的體現:
在這裡插入圖片描述

如上圖:以陣列 A = [2 5 3 0 2 3 0 3] 為例,說說計數排序的過程:

  1. 因為資料的範圍在 [0, 5],所以把資料分為 6 個桶,並存放在 C 陣列中(C 的下標代表資料,對應位置存放的是下標對應資料的數量);
  2. 然後對 C 中每個元素都求其前面元素與當前元素的和,即 C[i] = C[i - 1] + C[i];
  3. 請求一個存放排序好的資料的陣列,與 A 同樣大小;
  4. 從右往左對 A 排序,重複一下步驟:
    • R[C[A[n]] - 1] = A[n]
    • n = n - 1

計數排序的 Python 實現:

# copyright (c) strongnine

def countingSort(a):
    n = len(a)
    if n <= 1:
        return

    max = a[0]
    for i in range(1, n):
        if max < a[i]:
            max = a[i]


    c = [0 for i in range(max + 1)]

    for i in range(n):
        c[a[i]] += 1

    for i in range(1, max + 1):
        c[i] = c[i - 1] + c[i]

    r = [0 for i in range(n)]

    for i in range(n - 1, -1, -1):
        index = c[a[i]] - 1
        r[index] = a[i]
        c[a[i]] -= 1

    return r


if __name__ == '__main__':
    a = [2, 5, 3, 0, 2, 3, 0, 3]
    r = countingSort(a)
    print(r)

3. 基數排序(Radix sort)

類似於對手機號的排序,資料的範圍很大,所以要分桶就不太行,所以不適合上面兩種方式。

過程: 先按照最後一位使用穩定的線性排序來排序手機號碼,再按照倒數第二位排序,直到弄完全部 11 位。注意使用的排序演算法一定要是穩定的。

對於不一樣長的資料,例如對單詞的排序,可以在每個單詞後面都補零,再排序。

基數排序的要求:

  1. 資料的「位」需要可以獨立分割出來比較;
  2. 位之間有遞進關係,即高位比低位高,就可以忽略更低位的大小。

4. 線性排序的侷限

線性排序演算法對於資料的要求都比較嚴苛,應用並不是很廣泛,但是如果資料的特徵很適合使用線性排序,那麼用起來就會很高效。