基數(shù)排序有時(shí)也被稱作卡片排序,也是一種時(shí)間復(fù)雜度為O(n)的排序。為了給一個(gè)序列排序,基數(shù)排序需要大約相當(dāng)?shù)妮o助空間(通常使用隊(duì)列),可見空間復(fù)雜度為O(n) 假設(shè)有一個(gè)無序序列含有n 個(gè)整數(shù),整數(shù)的基數(shù)是k,最大有d (例如: 對于十進(jìn)制整數(shù)而言, k = 10,可能的值是 0, 1, 2, ..., 8, 9,在 32 位系統(tǒng)上 d10,在64 位系統(tǒng)上 d20),則基數(shù)排序的時(shí)間復(fù)雜度為O(d(n+k)) = O(n)。當(dāng)對整數(shù)排序時(shí),使用10進(jìn)制雖然比較容易理解,但是速度慢(因?yàn)闉榱说玫礁魑粩?shù)字需要做除法和取模等操作),為了盡可能的減小時(shí)間開銷,通常使用2 的指數(shù)次冪進(jìn)制(例如:2, 4, 8, ..., 2r,r2進(jìn)制的冪),這樣可以借助位移運(yùn)算減小耗費(fèi)CPU 指令數(shù),提升運(yùn)行速度。對于b(PC機(jī)上, b通常為3264)的整數(shù)而言, d = b/r k = 2r,于是時(shí)間復(fù)雜度O(d(n+k)) = O(b/r(n+2r)),理論上,當(dāng)b/r(n+2r)最小時(shí),速度應(yīng)該最快。

以一個(gè)隨機(jī)整數(shù)序列為例,基數(shù)排序的過程見下表。

178 207 982 510 477 295 963 095 274 614 810 579 700 618 301 766

待排序序序列

510 810 700 301 982 963 274 614 295 095 766 207 477 178 618 579 

個(gè)位排序結(jié)果

700 301 207 510 810 614 618 963 766 274 477 178 579 982 295 095

十位排序結(jié)果

095 178 207 274 295 301 477 510 579 614 618 700 766 810 963 982

百位排序結(jié)果


以上是以基數(shù)為
10進(jìn)行排序的例子,實(shí)現(xiàn)時(shí)應(yīng)該使用2的指數(shù)為基數(shù),從而避免除法和取模而采用位運(yùn)算來獲取較快的處理速度。
以下基數(shù)排序使用256個(gè)隊(duì)列,對int類型的序列從小到大排序,但是還是比快排慢一些,其實(shí)用多少隊(duì)列都比不上快排快。即使不用動態(tài)隊(duì)列,都用靜態(tài)隊(duì)列,還是比快排慢。根本原因在于這種基數(shù)排序的思想跟現(xiàn)代CPU架構(gòu)背道而馳,非常的不CPU cache friendly.從一個(gè)隊(duì)列換到另一隊(duì)列,然后再換回來,這種換來換去的機(jī)制導(dǎo)致CPU cache line不停的被重新填充,畢竟內(nèi)存的延遲和訪問速度比CPU cache要慢的多,大部分時(shí)間都耗費(fèi)在這里。結(jié)果這種看上去很理想的所謂的O(n)算法,只能徒有虛名。相信這些是那些純搞理論的算法專家和數(shù)學(xué)家永遠(yuǎn)都想不明白的!其實(shí)還不止基數(shù)排序如此,類似行為的計(jì)數(shù)排序和桶排序也快不起來,盡管它們的時(shí)間復(fù)雜度都是一色的O(n)。

 1 #include <queue> // use standard queue
 2 // non-portable implementation for LSD radix sort, for type int, 32 bit system
 3 template <typename ForwardIterator>
 4 void radix_sort(ForwardIterator first, ForwardIterator last)
 5 {
 6   // R: radix, K: k base, M: mask, B: ? bit system
 7   const unsigned R = 8, K = 1 << R, M = K - 1, B = 32;
 8   std::queue<int> q[K];
 9   for(unsigned i = 0; i < B; i += R)
10   {
11     for(ForwardIterator dit = first; dit != last; ++dit)
12     q[(*dit >> i) & M].push(*dit);
13     ForwardIterator cit = first;
14     for(unsigned j = 0; j < K; ++j)
15     while(!q[j].empty())
16     {
17       *cit++ = q[j].front();
18       q[j].pop();
19     }
20   }
21 }