无论是归并排序、堆排序或是快速排序,它们都是通过元素之间的比较进行排序,所以在这些排序方法的执行过程中,数组自身的性质(数组中各元素的排列顺序)会直接影响到时间或者内存成本的大小。这类排序算法称为比较排序,这些排序算法的下界都是
n
l
g
n
nlgn
nlgn。
计数排序不是比较排序,它的执行效果不依赖于愿数组本身元素的排列。
计数排序的思想是假设大小为
n
n
n的数组中的所有元素都是在区间
[
0
,
k
)
[0,k)
[0,k)上的整数,这样对于每一个元素
x
x
x,只要确定数组中小于
x
x
x的元素个数,我们就可以将
x
x
x放到正确的排序位置上。例如确定了有10个元素小于
x
x
x,这样的话,当输出结果序列时(从小到大排列的数组),我们就可以确定
x
x
x一定是在结果序列的第十一个位置。当然,如果有元素的值相同的话,符号上的细节处要略作修改,不能出现多个相同的值出现在同一个位置上。
对于一个有
n
n
n个元素的数组
l
i
s
t
0
list0
list0,对其进行计数排序时,需要一个对其元素数目进行记录的数组
l
i
s
t
1
list1
list1以及一个相同大小的输出数组
l
i
s
t
2
list2
list2。
具体的python实现如下。
#待排序数组为l0,数组元素值域为[0,k)
def counting_sort(l0,k):
#初始化结果数组l2,记录数组l1
l1=[0]*k
l2=[-1]*len(l0)
#遍历原数组,使用l1记录所有元素的个数,将元素值为t的个数记录在l1[t]中
for i in range(len(l0)):
l1[l0[i]]=l1[l0[i]]+1
#累加得到原数组中小于t的元素个数
for j in range(1,k,1):
l1[j]=l1[j]+l1[j-1]
#根据l1中得到的数据(小于t的元素个数)将每个元素放到结果数组l2中正确的位置上
for i in range(len(l0)-1,-1,-1):
#从原数组的最后向前循环可以保证:具有相同值的元素在输出数组中的相对位置与在元素组中一样
l2[l1[l0[i]]-1]=l0[i]
#个数减一,下一个相同的元素往前排
l1[l0[i]]=l1[l0[i]]-1
return l2
我借用《算法导论》里的例子来解释这个程序的运行。对于要排序的数组
l
0
=
[
2
,
5
,
3
,
0
,
2
,
3
,
0
,
3
]
l0=[2,5,3,0,2,3,0,3]
l0=[2,5,3,0,2,3,0,3],我们还需要知道的一个值就是所有元素的值域
[
0
,
k
)
[0,k)
[0,k),显然这里
k
=
6
k=6
k=6。
下面来展示下函数运行过程中,变量
l
1
l1
l1和
l
2
l2
l2变化情况。
对于数组
l
0
l0
l0,当
k
=
Θ
(
n
)
k=Θ(n)
k=Θ(n)时,排序的运行时间为
Θ
(
n
)
Θ(n)
Θ(n)。是一个很优秀的算法。