當輸入的元素是 n 個 0 到 k 之間的整數時,它的執行時間是 θ(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序演算法。
由於用來計數的陣列c的長度取決於待排序陣列中資料的範圍(等於待排序陣列的最大值與最小值的差加上1),這使得計數排序對於資料範圍很大的陣列,需要大量時間和記憶體。例如:計數排序是用來排序0到100之間的數字的最好的演算法,但是它不適合按字母順序排序人名。但是,計數排序可以用在基數排序中的演算法來排序資料範圍很大的陣列。
演算法的步驟如下:
找出待排序的陣列中最大和最小的元素
統計陣列中每個值為i的元素出現的次數,存入陣列c的第i項
對所有的計數累加(從c中的第乙個元素開始,每一項和前一項相加)
反向填充目標陣列:將每個元素i放在新陣列的第c(i)項,每放乙個元素就將c(i)減去1
貼上**:
[html]view plain
copy
#include
<
stdio.h
>
#include <
stdlib.h
>
#include <
time.h
>
//對於排序的關鍵字範圍,一定是0-99
#define num_range (100)
void print_arr(int *arr, int n)
else
} printf("\n");
} /*
演算法的步驟如下:
1.找出待排序的陣列中最大和最小的元素
2.統計陣列中每個值為i的元素出現的次數,存入陣列c的第i項
3.對所有的計數累加(從c中的第乙個元素開始,每一項和前一項相加)
4.反向填充目標陣列:將每個元素i放在新陣列的第c(i)項,每放乙個元素就將c(i)減去1
*/
void counting_sort(int *ini_arr, int *sorted_arr, int n)
for(i=0
; i<
n; i++)
for(k=1
; k<
num_range
; k++)
for(j=n
-1 ; j
>
=0; j--)
free(count_arr);
} int main(int argc, char* argv)
else
int i;
int *arr
= (int *)malloc(sizeof(int) * n);
int *sorted_arr
= (int *)malloc(sizeof(int) *n);
srand(time(0));
for(i=0
; i<
n; i++)
printf("ini_array: ");
print_arr(arr, n);
counting_sort(arr, sorted_arr, n);
printf("sorted_array: ");
print_arr(sorted_arr, n);
free(arr);
free(sorted_arr);
return 0;
}
桶排序:
桶排序的基本思想
假設有一組長度為n的待排關鍵字序列k[1....n]。首先將這個序列劃分成m個的子區間(桶) 。然後基於某種對映函式 ,將待排序列的關鍵字k對映到第i個桶中(即桶陣列b的下標 i) ,那麼該關鍵字k就作為b[i]中的元素(每個桶b[i]都是一組大小為n/m的序列)。接著對每個桶b[i]中的所有元素進行比較排序(可以使用快排)。然後依次列舉輸出b[0]....b[m]中的全部內容即是乙個有序序列。
假如待排序列k= 。這些資料全部在1—100之間。因此我們定製10個桶,然後確定對映函式f(k)=k/10。則第乙個關鍵字49將定位到第4個桶中(49/10=4)。依次將所有關鍵字全部堆入桶中,並在每個非空的桶中進行快速排序。
桶排序代價分析
桶排序利用函式的對映關係,減少了幾乎所有的比較工作。實際上,桶排序的f(k)值的計算,其作用就相當於快排中劃分,已經把大量資料分割成了基本有序的資料塊(桶)。然後只需要對桶中的少量資料做先進的比較排序即可。
對n個關鍵字進行桶排序的時間複雜度分為兩個部分:
(1)迴圈計算每個關鍵字的桶對映函式,這個時間複雜度是o(n)。
(2) 利用先進的比較排序演算法對每個桶內的所有資料進行排序,其時間複雜度為 ∑ o(ni*logni) 。其中ni 為第i個桶的資料量。
很顯然,第(2)部分是桶排序效能好壞的決定因素。儘量減少桶內資料的數量是提高效率的唯一辦法(因為基於比較排序的最好平均時間複雜度只能達到o(n*logn)了)。因此,我們需要盡量做到下面兩點:
(1) 對映函式f(k)能夠將n個資料平均的分配到m個桶中,這樣每個桶就有[n/m]個資料量。
(2) 盡量的增大桶的數量。極限情況下每個桶只能得到乙個資料,這樣就完全避開了桶內資料的「比較」排序操作。 當然,做到這一點很不容易,資料量巨大的情況下,f(k)函式會使得桶集合的數量巨大,空間浪費嚴重。這就是乙個時間代價和空間代價的權衡問題了。
對於n個待排資料,m個桶,平均每個桶[n/m]個資料的桶排序平均時間複雜度為:
o(n)+o(m*(n/m)*log(n/m))=o(n+n*(logn-logm))=o(n+n*logn-n*logm)
當n=m時,即極限情況下每個桶只有乙個資料時。桶排序的最好效率能夠達到o(n)。
總結: 桶排序的平均時間複雜度為線性的o(n+c),其中c=n*(logn-logm)。如果相對於同樣的n,桶數量m越大,其效率越高,最好的時間複雜度達到o(n)。 當然桶排序的空間複雜度 為o(n+m),如果輸入資料非常龐大,而桶的數量也非常多,則空間代價無疑是昂貴的。此外,桶排序是穩定的。
基數排序
上面的問題是多關鍵字的排序,但單關鍵字也仍然可以使用這種方式。
比如字串「abcd」 「aesc」 "dwsc" "rews"就可以把每個字元看成乙個關鍵字。另外還有整數 425、321、235、432也可以每個位上的數字為乙個關鍵字。
基數排序的思想就是將待排資料中的每組關鍵字依次進行桶分配。比如下面的待排序列:
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個左右。
但是,對比桶排序,基數排序每次需要的桶的數量並不多。而且基數排序幾乎不需要任何「比較」操作,而桶排序在桶相對較少的情況下,桶內多個資料必須進行基於比較操作的排序。因此,在實際應用中,基數排序的應用範圍更加廣泛。
計數排序 桶排序和基數排序
當輸入的元素是 n 個 0 到 k 之間的整數時,它的執行時間是 n k 計數排序不是比較排序,排序的速度快於任何比較排序演算法。由於用來計數的陣列c的長度取決於待排序陣列中資料的範圍 等於待排序陣列的最大值與最小值的差加上1 這使得計數排序對於資料範圍很大的陣列,需要大量時間和記憶體。例如 計數排...
桶排序 計數排序和基數排序
我們把時間複雜度為o n 的排序演算法,稱為線性排序。意思是,排序時間和排序個數成正比。桶排序就是說,我把n個數,分為m個桶,每個桶裡k個數,k n m。桶的大小順序已經定義好了。然後把桶區間裡的數字扔到桶裡。最後進行桶內快速排序。所以桶的時間複雜度是o m k log n m 又因為k n m,所...
桶排序 基數排序 計數基數排序 Java
前面已經講述了很多排序演算法,但是他們的排序演算法都是基於兩個值之間的比較,通過決策樹的方法可以證明深度為d的二叉樹則最多有 一些好的排序演算法是可以達到時間複雜度是線性的,桶排序就是其中一種。比如有n個數,但是這些數的最大數不超過m。這個時候就可以定義乙個含有m個元素的陣列 初始值為0 然後遍歷n...