最近面試了一些人,發現大家都忽略了排序演算法中的計數排序、桶排序和基數排序,segmentfault中都沒有它們的標籤就是一明證,呵呵!
計數排序
當輸入的元素是 n 個 0 到 k 之間的整數時,它的執行時間是 Θ(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序演算法。
由於用來計數的陣列C的長度取決於待排序陣列中資料的範圍(等於待排序陣列的最大值與最小值的差加上1),這使得計數排序對於資料範圍很大的陣列,需要大量記憶體。計數排序是用來排序0到100之間的數字的最好的演算法,但是它不適合按字母順序排序人名。但是,計數排序可以用在基數排序中的演算法來排序資料範圍很大的陣列。
演算法的步驟如下:
- 找出待排序的陣列中最大和最小的元素
- 統計陣列中每個值為i的元素出現的次數,存入陣列C的第i項
- 對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加)
- 反向填充目標陣列:將每個元素i放在新陣列的第C(i)項,每放一個元素就將C(i)減去1
#define NUM_RANGE (100) //預定義資料範圍上限,即K的值
void counting_sort(int *ini_arr, int *sorted_arr, int n) //所需空間為 2*n+k
{
int *count_arr = (int *)malloc(sizeof(int) * NUM_RANGE);
int i, j, k;
//初始化統計陣列元素為值為零
for(k=0; k<NUM_RANGE; k++){
count_arr[k] = 0;
}
//統計陣列中,每個元素出現的次數
for(i=0; i<n; i++){
count_arr[ini_arr[i]]++;
}
//統計陣列計數,每項存前N項和,這實質為排序過程
for(k=1; k<NUM_RANGE; k++){
count_arr[k] += count_arr[k-1];
}
//將計數排序結果轉化為陣列元素的真實排序結果
for(j=n-1 ; j>=0; j--){
int elem = ini_arr[j]; //取待排序元素
int index = count_arr[elem]-1; //待排序元素在有序陣列中的序號
sorted_arr[index] = elem; //將待排序元素存入結果陣列中
count_arr[elem]--; //修正排序結果,其實是針對算得元素的修正
}
free(count_arr);
}
桶排序
我的理解:
桶排序是計數排序的變種,把計數排序中相鄰的m個"小桶"放到一個"大桶"中,在分完桶後,對每個桶進行排序(一般用快排),然後合併成最後的結果。
基本思想:
桶排序假設序列由一個隨機過程產生,該過程將元素均勻而獨立地分佈在區間[0,1)上。我們把區間[0,1)劃分成n個相同大小的子區間,稱為桶。將n個記錄分佈到各個桶中去。如果有多於一個記錄分到同一個桶中,需要進行桶內排序。最後依次把各個桶中的記錄列出來記得到有序序列。
效率分析:
桶排序的平均時間複雜度為線性的O(N+C),其中C為桶內快排的時間複雜度。如果相對於同樣的N,桶數量M越大,其效率越高,最好的時間複雜度達到O(N)。 當然桶排序的空間複雜度 為O(N+M),如果輸入資料非常龐大,而桶的數量也非常多,則空間代價無疑是昂貴的。此外,桶排序是穩定的。
基數排序
基本思想:
將待排資料中的每組關鍵字依次進行桶分配。
具體示例:
278、109、063、930、589、184、505、269、008、083
我們將每個數值的個位,十位,百位分成三個關鍵字: 278 -> k1(個位)=8,k2(十位)=7,k3=(百位)=2。
然後從最低位個位開始(從最次關鍵字開始),對所有資料的k1關鍵字進行桶分配(因為,每個數字都是 0-9的,因此桶大小為10),再依次輸出桶中的資料得到下面的序列。
930、063、083、184、505、278、008、109、589、269
再對上面的序列接著進行鍼對k2的桶分配,輸出序列為:
505、008、109、930、063、269、278、083、184、589
最後針對k3的桶分配,輸出序列為:
008、063、083、109、184、269、278、505、589、930
效率分析:
基數排序的效能比桶排序要略差。每一次關鍵字的桶分配都需要O(N)的時間複雜度,而且分配之後得到新的關鍵字序列又需要O(N)的時間複雜度。假如待排資料可以分為d個關鍵字,則基數排序的時間複雜度將是O(d*2N) ,當然d要遠遠小於N,因此基本上還是線性級別的。基數排序的空間複雜度為O(N+M),其中M為桶的數量。一般來說N>>M,因此額外空間需要大概N個左右。
但是,對比桶排序,基數排序每次需要的桶的數量並不多。而且基數排序幾乎不需要任何“比較”操作,而桶排序在桶相對較少的情況下,桶內多個資料必須進行基於比較操作的排序。因此,在實際應用中,基數排序的應用範圍更加廣泛。