最近面試了一些人,發現你們都忽略了排序算法中的計數排序、桶排序和基數排序,segmentfault中都沒有它們的標籤就是一明證,呵呵!面試
當輸入的元素是 n 個 0 到 k 之間的整數時,它的運行時間是 Θ(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序算法。算法
因爲用來計數的數組C的長度取決於待排序數組中數據的範圍(等於待排序數組的最大值與最小值的差加上1),這使得計數排序對於數據範圍很大的數組,須要大量內存。計數排序是用來排序0到100之間的數字的最好的算法,可是它不適合按字母順序排序人名。可是,計數排序能夠用在基數排序中的算法來排序數據範圍很大的數組。segmentfault
算法的步驟以下:數組
#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個"小桶"放到一個"大桶"中,在分完桶後,對每一個桶進行排序(通常用快排),而後合併成最後的結果。code
基本思想:排序
桶排序假設序列由一個隨機過程產生,該過程將元素均勻而獨立地分佈在區間[0,1)上。咱們把區間[0,1)劃分紅n個相同大小的子區間,稱爲桶。將n個記錄分佈到各個桶中去。若是有多於一個記錄分到同一個桶中,須要進行桶內排序。最後依次把各個桶中的記錄列出來記獲得有序序列。內存
效率分析:效率
桶排序的平均時間複雜度爲線性的O(N+C),其中C爲桶內快排的時間複雜度。若是相對於一樣的N,桶數量M越大,其效率越高,最好的時間複雜度達到O(N)。 固然桶排序的空間複雜度 爲O(N+M),若是輸入數據很是龐大,而桶的數量也很是多,則空間代價無疑是昂貴的。此外,桶排序是穩定的。統計
基本思想:
將待排數據中的每組關鍵字依次進行桶分配。
具體示例:
27八、10九、06三、930、58九、18四、50五、26九、00八、083
咱們將每一個數值的個位,十位,百位分紅三個關鍵字: 278 -> k1(個位)=8,k2(十位)=7,k3=(百位)=2。
而後從最低位個位開始(從最次關鍵字開始),對全部數據的k1關鍵字進行桶分配(由於,每一個數字都是 0-9的,所以桶大小爲10),再依次輸出桶中的數據獲得下面的序列。
930、06三、08三、18四、50五、27八、00八、10九、58九、269
再對上面的序列接着進行鍼對k2的桶分配,輸出序列爲:
50五、00八、10九、930、06三、26九、27八、08三、18四、589
最後針對k3的桶分配,輸出序列爲:
00八、06三、08三、10九、18四、26九、27八、50五、58九、930
效率分析:
基數排序的性能比桶排序要略差。每一次關鍵字的桶分配都須要O(N)的時間複雜度,並且分配以後獲得新的關鍵字序列又須要O(N)的時間複雜度。假如待排數據能夠分爲d個關鍵字,則基數排序的時間複雜度將是O(d*2N) ,固然d要遠遠小於N,所以基本上仍是線性級別的。基數排序的空間複雜度爲O(N+M),其中M爲桶的數量。通常來講N>>M,所以額外空間須要大概N個左右。
可是,對比桶排序,基數排序每次須要的桶的數量並很少。並且基數排序幾乎不須要任何「比較」操做,而桶排序在桶相對較少的狀況下,桶內多個數據必須進行基於比較操做的排序。所以,在實際應用中,基數排序的應用範圍更加普遍。