基本思想
基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。由於整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是隻能使用於整數。
基數排序可以採用兩種方式:
- LSD(Least Significant Digital):從待排序元素的最右邊開始計算(如果是數字類型,即從最低位個位開始)。
- MSD(Most Significant Digital):從待排序元素的最左邊開始計算(如果是數字類型,即從最高位開始)。
我們以LSD方式爲例,從數組R[1..n]中每個元素的最低位開始處理,假設基數爲radix,如果是十進制,則radix=10。基本過程如下所示:
- 計算R中最大的元素,求得位數最大的元素,最大位數記爲distance;
- 對每一位round<=distance,計算R[i] % radix即可得到;
- 將上面計算得到的餘數作爲bucket編號,每個bucket中可能存放多個數組R的元素;
- 按照bucket編號的順序,收集bucket中元素,就地替換數組R中元素;
- 重複2~4,最終數組R中的元素爲有序。
算法實現
基數排序算法,Java實現,代碼如下所示:
01 |
public abstract class Sorter
{ |
02 |
public abstract void sort( int []
array); |
03 |
} |
04 |
05 |
public class RadixSorter extends Sorter
{ |
06 |
|
07 |
private int radix; |
08 |
|
09 |
public RadixSorter()
{ |
10 |
radix
= 10 ; |
11 |
} |
12 |
|
13 |
@Override |
14 |
public void sort( int []
array) { |
15 |
//
數組的第一維表示可能的餘數0-radix,第二維表示array中的等於該餘數的元素 |
16 |
//
如:十進制123的個位爲3,則bucket[3][] = {123} |
17 |
int [][]
bucket = new int [radix][array.length]; |
18 |
int distance
= getDistance(array); //
表示最大的數有多少位 |
19 |
int temp
= 1 ; |
20 |
int round
= 1 ; //
控制鍵值排序依據在哪一位 |
21 |
while (round
<= distance) { |
22 |
//
用來計數:數組counter[i]用來表示該位是i的數的個數 |
23 |
int []
counter = new int [radix]; |
24 |
//
將array中元素分佈填充到bucket中,並進行計數 |
25 |
for ( int i
= 0 ;
i < array.length; i++) { |
26 |
int which
= (array[i] / temp) % radix; |
27 |
bucket[which][counter[which]]
= array[i]; |
28 |
counter[which]++; |
29 |
} |
30 |
int index
= 0 ; |
31 |
//
根據bucket中收集到的array中的元素,根據統計計數,在array中重新排列 |
32 |
for ( int i
= 0 ;
i < radix; i++) { |
33 |
if (counter[i]
!= 0 ) |
34 |
for ( int j
= 0 ;
j < counter[i]; j++) { |
35 |
array[index]
= bucket[i][j]; |
36 |
index++; |
37 |
} |
38 |
counter[i]
= 0 ; |
39 |
} |
40 |
temp
*= radix; |
41 |
round++; |
42 |
} |
43 |
} |
44 |
|
45 |
private int getDistance( int []
array) { |
46 |
int max
= computeMax(array); |
47 |
int digits
= 0 ; |
48 |
int temp
= max / radix; |
49 |
while (temp
!= 0 )
{ |
50 |
digits++; |
51 |
temp
= temp / radix; |
52 |
} |
53 |
return digits
+ 1 ; |
54 |
} |
55 |
|
56 |
private int computeMax( int []
array) { |
57 |
int max
= array[ 0 ]; |
58 |
for ( int i= 1 ;
i<array.length; i++) { |
59 |
if (array[i]>max)
{ |
60 |
max
= array[i]; |
61 |
} |
62 |
} |
63 |
return max; |
64 |
} |
65 |
} |
基數排序算法,Python實現,代碼如下所示:
01 |
class Sorter: |
02 |
''' |
03 |
Abstract
sorter class, which provides shared methods being used by |
04 |
subclasses. |
05 |
''' |
06 |
__metaclass__ = ABCMeta |
07 |
|
08 |
@abstractmethod |
09 |
def sort( self ,
array): |
10 |
pass |
11 |
12 |
class RadixSorter(Sorter): |
13 |
''' |
14 |
Radix
sorter |
15 |
''' |
16 |
def __init__( self ): |
17 |
self .radix = 10 |
18 |
|
19 |
def sort( self ,
array): |
20 |
length = len (array) |
21 |
which_round = 1 |
22 |
bucket = [[ 0 for col in range (length)] for row in range ( self .radix)] |
23 |
distance = self .__get_distance(array) |
24 |
temp = 1 |
25 |
while which_round< = distance: |
26 |
counter = [ 0 for x in range ( self .radix)] |
27 |
for i in range (length): |
28 |
which = (array[i] / / temp) % self .radix |
29 |
bucket[which][counter[which]] = array[i] |
30 |
counter[which] + = 1 |
31 |
index = 0 |
32 |
for i in range ( self .radix): |
33 |
if counter[i]! = 0 : |
34 |
for j in range (counter[i]): |
35 |
array[index] = bucket[i][j] |
36 |
index + = 1 |
37 |
temp * = self .radix |
38 |
which_round + = 1 |
39 |
|
40 |
41 |
def __get_distance( self ,
array): |
42 |
max_elem = self .__get_max(array) |
43 |
digits = 0 |
44 |
temp = max_elem / / self .radix |
45 |
while temp
! = 0 : |
46 |
digits + = 1 |
47 |
temp / / = self .radix |
48 |
return digits + 1 |
49 |
|
50 |
def __get_max( self ,
array): |
51 |
max_elem = array[ 0 ] |
52 |
for x in range ( 1 , len (array)): |
53 |
if array[x]>max_elem: |
54 |
max_elem = array[x] |
55 |
return max_elem |
排序過程
假設待排序數組爲array = {94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49},數組大小爲20,我們以該數組爲例,
最大的數組元素的位數爲2,所以需要進行2輪映射(映射到對應的桶中),執行基數排序的具體過程,如下所示:
- 數組原始順序
數組的原始順序,如下圖所示:
數組中存在的相同的元素(同一個待排序的數字出現大於1次),我們使用不同的背景顏色來區分(紅色背景表示第二次出現,靛青色表示第三次出現),如果一個元素只出現過一次,則我們就使用一種固定的顏色(淺綠色)表示。
根據數組元素個位數字將數組中元素映射到對應的桶中(bucket)
我們使用的是十進制,基數(Radix)自然是10,根據數組元素個位數的,應該映射到10個桶中,映射後的結果,如圖所示:
在映射到桶的過程中,從左到右掃描原始數組。因爲映射到同一個桶中的元素可能存在多個,最多爲整個數組的長度,所以在同一個桶中,要保持進入桶中的元素的先後順序(先進的排在左側,後進的排在右側)。
- 收集桶中元素,並在原始數組中原地替換,使數組中元素順序重新分佈
掃面前面已經映射到各個桶中的元素,滿足這樣的順序:先掃描編號最小的桶,桶中如果存在多個元素,必須按照從左到右的順序。這樣,將得到的數組元素重新分佈,得到一個元素位置重新分佈的數組,如圖所示:
這時,可以看到元素實際上是按照個位的數字進行了排序,但是基於整個元素來說並不是有序的。
- 根據數組元素十位數字將數組中元素映射到對應的桶中(bucket)
這次映射的原則和過程,與前面類似,不同的是,這次掃描的數組是經過個位數字處理重新分佈後的新數組,映射後桶內的狀態,如圖所示:
- 收集桶中元素,並在原始數組中原地替換,使數組中元素順序重新分佈
和前面收集方法類似,得到的數組及其順序,如圖所示:
我們可以看到,經過兩輪映射和收集過程,數組已經變成有序了,排序結束。
算法分析
- 時間複雜度
設待排序的數組R[1..n],數組中最大的數是d位數,基數爲r(如基數爲10,即10進制,最大有10種可能,即最多需要10個桶來映射數組元素)。處理一位數,需要將數組元素映射到r個桶中,映射完成後還需要收集,相當於遍歷數組一遍,最多元素書爲n,則時間複雜度爲O(n+r)。所以,總的時間複雜度爲O(d*(n+r))。
- 空間複雜度
設待排序的數組R[1..n],數組中最大的數是d位數,基數爲r。基數排序過程中,用到一個計數器數組,長度爲r,還用到一個r*n的二位數組來做爲桶,所以空間複雜度爲O(r*n)。
- 排序穩定性
通過上面的排序過程,我們可以看到,每一輪映射和收集操作,都保持從左到右的順序進行,如果出現相同的元素,則保持他們在原始數組中的順序。
可見,基數排序是一種穩定的排序。
轉載鏈接:http://shiyanjun.cn/archives/823.html