假设要排序的数组为 A = {1,0,3,1,0,1,1}这里最大值为3,最小值为0,
那么我们创建一个数组C,长度为3+1-0=4。然后一趟扫描数组A,得到A中各个元素的总数,并保持到数组C的对应单元中。比如0 的出现次数为2次,则 C[0] = 2;1 的出现次数为4次,则C[1] = 4。C=[2,4,0,1]由于C 是以A的元素为下标的,所以这样一做,A中的元素在C中自然就成为有序的了,然后我们分别统计比0,1,2,3小的元素个数,如比1小(包括1)的元素有6个。更新C,C=[2,6,6,7],更新C是为了保证排序稳定。然后把这个在C中的记录按每个元素的计数展开到输出数组B中,排序就完成了。 也就是B[0] 到 B[1] 为0, B[2] 到 B[5] 为1 这样依此类推。
元素 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|
元素下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
举例讲解
元素 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|
元素下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
第二个元素是4,则下标为4的元素加1:
元素 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|
元素下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
第三个元素是2,则下标为2的元素加1:
元素 | 0 | 0 | 2 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|
元素下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
如此遍历原数组,最终结果:
元素 | 3 | 2 | 2 | 1 | 2 | 2 | 1 | 2 | 1 | 2 | 2 |
---|---|---|---|---|---|---|---|---|---|---|---|
元素下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
这样就统计出了数组中每个值出现的次数,有了这个统计结果就可以直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次
def count_sort1(arr):
maxi = max(arr) # 找出最大值
count_arr = [0 for _ in range(maxi + 1)] # 创建一个全0列表
# 统计序列中每个元素出现的次数
for value in arr:
count_arr[value] += 1
arr.clear() # 将原列表清空
for index, element in enumerate(count_arr): # index:元素下标
for i in range(element):
arr.append(index)
def count_sort2(arr):
maxi = max(arr) # 找出数组中的最大值
count_arr = [0 for _ in range(maxi + 1)] # 创建一个全0列表
# 统计序列中每个元素出现的次数
for value in arr:
count_arr[value] += 1
new_arr = [] # 创建一个新列表
for i in range(len(count_arr)):
while count_arr[i] != 0:
new_arr.append(i)
count_arr[i] -= 1
return new_arr
import random
numbers = [random.randint(0, 10) for _ in range(20)]
print(numbers)
count_sort1(numbers)
print(numbers)
numbers2=[3,4,5,6,9,5,2,1,2,3,6,8,9,7]
print(count_sort2(numbers2))
代码中的一些解释
1 start sequence[0]
2 start+1 sequence[1]
3 start+2 sequence[2]
...
实例:
seq = ['one', 'two', 'three']
for i, element in enumerate(seq):
print(i, element)
结果输出:
0 one
1 two
2 three
这个函数会输出下元素值和其对应的下标索引
list1 = [0 for _ in range(10)]
print(list)
list2=[2**2 for _ in range(5)]
print(list2)
输出结果:
list1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
list2:[2, 2, 2, 2, 2]
这里的下划线(_)是个占位符,不用管它里面什么内容,它的作用就是让前面的内容循环这么多次, 即 for _ in range(n)的作用是仅仅是重复循环 n 次
上面的代码在一开始就求得了数列的最大值maxi,创建的数组count_arr,长度是maxi+1,以保证数组最后一个下标是maxi。
但是这段代码并不严谨,假如数组中的最小值是50,最大值是59,那么按照上面代码则需要创建长度为60的数组,为前面50个数(0-49)是没有的,那么就会浪费空间,所以数组长度应该是 : 最大值-最小值+1,而把数组最小值作为一个偏移量。
举例说明:假如数组为:55,54,59,50,51,52,51,53,56,55
则偏移量=最小值(50),那么对于第一个数55而言,对应的数组下标应该是55-50=5
为了使排序是稳定的,我们可以依次统计比**[50,51,52, … ,59]小的元素个数,例如比 51小的元素个数(包括51)有3个
原来的计数数组为:
元素次数 | 1 | 2 | 1 | 1 | 1 | 2 | 1 | 0 | 0 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
偏移后下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
元素下标 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
相加后的计数数组为
相加的原则使每个元素出现的次数=它前面所有元素出现次数+该元素出现的次数
相加次数 | 1 | 3 | 4 | 5 | 6 | 8 | 9 | 9 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
偏移后下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
元素下标 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
相加后计数数组元素的值就等于元素的所在位置,比如下标是6的元素值是9,代表原数列的值56最终的排序是在第9位
优化后的代码:
def count_sort2(arr):
maxi = max(arr)
mini = min(arr)
count_arr_length = maxi - mini + 1 # 计算待排序列表的数值区域长度,如4-9共9+1-4=6个数
count_arr = [0 for _ in range(count_arr_length)] # 创建一个全为0的列表
for value in arr:
count_arr[value - mini] += 1 # 统计列表中每个值出现的次数
# 使count_arr[i]存放<=i的元素个数,就是待排序列表中比某个值小的元素有多少个
for i in range(1, count_arr_length):
count_arr[i] = count_arr[i] + count_arr[i - 1]
new_arr = [0 for _ in range(len(arr))] # 存放排序结果
for i in range(len(arr)-1, -1, -1): # 从后往前循环是为了使排序稳定
new_arr[count_arr[arr[i] - mini] - 1] = arr[i] # -1是因为下标是从0开始的
count_arr[arr[i] - mini] -= 1 # 每归位一个元素,就少一个元素
return new_arr
if __name__ == '__main__':
user_input = input('输入待排序的数,用\",\"分隔:\n').strip()
#strip() 方法用于移除字符串头尾指定的字符(默认为空格)
unsorted = [int(item) for item in user_input.split(',')]
print(count_sort2(unsorted))
这里我说的比较可能不是太易懂,关于计数排序的更详细介绍过程可以点击这里查看
计数排序适用于最大值和最小值相差不大的列表,如果一个列表的最小值是1,最大值是1亿,那么计数排序就需要开一个长度为1亿+1的列表,会比较费时