1. 程式人生 > >【演算法導論】8.線性時間排序(計數排序,基數排序,桶排序)

【演算法導論】8.線性時間排序(計數排序,基數排序,桶排序)

三種線性時間複雜度的排序演算法:計數排序,基數排序,桶排序。


8.1排序演算法的下界

給定兩個元素ai和aj,如果使用比較排序,可以有ai<aj,ai<=aj,ai=aj,ai>aj,ai>=aj五種操作來確定其相對次序。本節假設所有的比較採用ai<=aj形式。

決策樹模型:

決策樹是一顆完全二叉樹,其葉結點列出了所有比較的可能。在下圖決策樹中,表示對於三個元素的排序,存在<1,2,3>,<1,3,2>,<3,1,2>,<2,1,3>,<2,3,1>,<3,2,1>這六種,其中1,2,3表示元素的下標。例如<1,3,2>表示a1<=a3<=a2。排序演算法的執行對應於一條從樹的根結點到葉結點的路徑。

 

在決策樹中,從根結點到任意一個可達葉結點之間最長簡單路徑的長度,表示的是對應的排序演算法中最壞的情況下的次數。因此,一個比較排序演算法中最壞情況的比較次數是樹的高度。所以可以給出一個下界:任何比較排序演算法都需要做\Omega(nlgn)次比較。

堆排序和歸併排序都是漸進最優的比較排序演算法。


8.2計數排序

計數排序假設n個輸入元素中的每一個都是在0到k區間的一個整數,其中k為某個整數。當k=O(n)時,排序的執行時間為\Theta(n)。

思想:對每一個輸入元素x,確定小於x的元素個數。利用這個資訊直接把x放在它輸出陣列的位置上。如果有幾個相同元素時,需要做出修改。

程式碼:假設輸入為A[1..n],A.length=n。B[1..n]存放排序的輸出,C[0..k]提供臨時儲存空間。

Counting-Sort(A,B,k){
    let C[0..k] be a new array;
    for i=0 to k
    {
        C[i]=0;//初始化陣列C並置為0
    }
    for j=1 to A.length
        C[A[j]]=C[A[j]]+1;//C中儲存等於i的元素的個數
    for i=1 to k
        C[i]=C[i]+C[i-1];//C中儲存小於或等於i的元素個數
    for j=A.length downto 1
    {
        B[C[A[j]]]=A[j];//把A中的元素放到B中正確的位置
        C[A[j]]--;//每放好一個需要減1,因為如果出現了值相同的多個元素,不能放在同一個位置,減1後就放在了前一個位置
    }
}

總的執行時間代價O(k+n)。在實際中,當k=O(n),一般會採用基數排序,這時執行時間為\Theta(n)。計數排序是穩定的(所以可以用於基數排序的子過程)。


8.3基數排序

基數排序是先按最低有效位進行排序來解決卡片排序問題。然後演算法將所有卡片合併成一疊。重複對d位數字都進行排序,此時所有的卡片已按d位數完全排好序。對這一疊卡片的排序僅需要進行d輪(d為位數)。為了確保基數排序的正確性,一位數排序演算法必須是穩定的,即保證排序過程以及從容器中取出時不改變順序。

假設n個d位的元素存放在陣列A中,其中第1位是最低位,第d位是最高位:

Radix-Sort(A, d)
{
    for i=1 to d
        use a stable sort to sort array A on digit i;
}

這個排序演算法非常直觀,就是分別對每一位使用穩定的排序演算法排好。

給定n個d位數,其中每一個數位有k個可能的取值。如果Radix-Sort使用的穩定排序演算法耗時\Theta(n+k),那麼它可以在\Theta(d(n+k))時間內將這些數排好序。當d為常數且k=O(n)時,基數排序具有線性的時間代價。

給定n個b位數和任何正整數r<=b,如果Radix-Sort使用的穩定排序演算法對資料的取值區間是0到k 的輸入進行排序耗時\Theta(n+k),那麼它就可以在\Theta((b/r)(n+2^r))時間內將這些數排好序。


8.4桶排序

桶排序假設輸入資料服從均勻分佈,平均時間代價為O(n)。

計數排序與桶排序的區別:計數排序假設輸入的資料都屬於一個小區間內的整數,桶排序假設輸入是由一個隨機過程產生,該過程將元素均勻、獨立地分佈在[0,1)區間上。它們的主要區別就是用於排序的資料服從不同的分佈。

將[0,1)區間劃分為n個相同大小的子區間,或稱為桶。將n個輸入數分別放到各個桶中。因為輸入資料是均勻、獨立分佈在[0,1)區間上,所以一般不會出現很多數落在同一個桶的情況。為了得到輸出結果,我們先對每個桶中的數進行排序,然後遍歷每個桶,按照次序把各個桶中的元素列出來。

假設輸入為有n個元素的陣列A,每個元素A[i]滿足0<=A[i]<1。臨時陣列B[0..n-1]用於存放連結串列(即桶),並假設存在一種用於維護這些連結串列的機制:

Bucket-Sort(A)
{
    n=A.length;
    let B[0..n-1] be a new array;
    for i=0 to n-1
        make B[i] an empty list//初始化一個空陣列B
    for I=1 to n
        insert A[i] into list B[nA[i]];//A是[0,1)的分佈,乘n後向下取整放進B中
    for I=0 to n-1
        sort list B[i] with insertion sort
    concatenate the list B[0],B[1],...,B[n-1] together in order//取出排序後的元素
}

執行的時間期望為\Theta(n),即使輸入資料不服從均勻分佈,桶排序也仍然可以線上性時間內完成。