本文共 4453 字,大约阅读时间需要 14 分钟。
基本思想
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
基数排序可以采用两种方式:我们以LSD方式为例,从数组R[1..n]中每个元素的最低位开始处理,假设基数为radix,如果是十进制,则radix=10。基本过程如下所示:
算法实现
基数排序算法,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个桶中,映射后的结果,如图所示:
在映射到桶的过程中,从左到右扫描原始数组。因为映射到同一个桶中的元素可能存在多个,最多为整个数组的长度,所以在同一个桶中,要保持进入桶中的元素的先后顺序(先进的排在左侧,后进的排在右侧)。扫面前面已经映射到各个桶中的元素,满足这样的顺序:先扫描编号最小的桶,桶中如果存在多个元素,必须按照从左到右的顺序。这样,将得到的数组元素重新分布,得到一个元素位置重新分布的数组,如图所示:
这时,可以看到元素实际上是按照个位的数字进行了排序,但是基于整个元素来说并不是有序的。这次映射的原则和过程,与前面类似,不同的是,这次扫描的数组是经过个位数字处理重新分布后的新数组,映射后桶内的状态,如图所示:
和前面收集方法类似,得到的数组及其顺序,如图所示:
我们可以看到,经过两轮映射和收集过程,数组已经变成有序了,排序结束。算法分析
设待排序的数组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://psssx.baihongyu.com/