計數排序 桶排序和基數排序

2021-07-22 15:30:58 字數 4126 閱讀 8368

當輸入的元素是 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...