一、什么是计数排序
字面意思理解就是将序列当中的元素出现的个数进行记录,然后再进行排序。
比如说有一组数列,里面有10个数字,这十个数字的范围是在0~8之间,那么如何才能在最短时间内将这个数组排列完成呢?
由于这个数字的取值范围是在0~8,所以先建立一个长度为元素取值最大值+1的一个数组-count_list,下标从0-8,元素的初始值先设置为0
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
这里有这样的一个数组-array_list
[ 0, 8, 3, 1, 5, 7, 3, 6, 4, 4]
1、首先先遍历整个数列,到第一个元素的时候,遍历到的内容是数字0,那么count_list下标为零的位置所对应的元素值加1
[ 0, 8, 3, 1, 5, 7, 3, 6, 4, 4]
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
2、那么同理,第二个遍历到的数字是8,那么就在count_list列表当中下标为8的位置元素加1
[ 0, 8, 3, 1, 5, 7, 3, 6, 4, 4]
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
3、接着往下走,遍历到了3,那么在count_list下标为3的位置元素加1
[ 0, 8, 3, 1, 5, 7, 3, 6, 4, 4]
1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
之后的操作同理即可,元素出现一次,就在对应位置下标的元素值上加1,最后遍历完成的count_list就变成了这个样子
1 | 1 | 0 | 2 | 2 | 1 | 1 | 1 | 1 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
那么每个数字出现了多少次包括具体的数字是几,就一目了然了,之后我们所需要做的就是遍历count_list列表,输出下标,下标对应的元素是几就打印这个下标几次,
[ 0, 1, 3, 3, 4, 4, 5, 6, 7, 8]
二、 原理清楚之后上代码
# 创建随机列表函数
import random
array_list = []
def input_num():
'''
控制产生随机数列表
:return: array_list
'''
global num, start, end
num = int(input('请输入产生数组的个数:'))
if num <= 0:raise Exception('请输入大于零的数字')
start = int(input('请输入起始数字:'))
end = int(input('请输入结束数字:'))
if start > end :raise Exception('起始数字一定要小于结束数字')
for i in range(num):
n = random.randint(start, end)
array_list.append(n)
return array_list
随机的列表创建好之后,开始计数排序
array_list = input_num()
print("生成的随机数组:\n%s"% array_list)
res = []
def count_sort(array_list):
# 先得到最大元素确定count_list的范围
max_value = array_list[0]
for i in range(len(array_list)):
if array_list[i] > max_value:
max_value = array_list[i]
# 创建0-最大元素之间的空列表
count_list = [0] * (max_value + 1)
# 遍历生成的随机列表,把出现的元素,作为count_list列表的下标,
# 其中,对应下标的元素值+1
for i in array_list:
count_list[i] += 1
# 做好标记之后按照顺序输出
# 在count_list当中下标是几就输出几,下标就是在原来列表中的数字
# count_list当中的元素个数是代表下标重复输出的次数
for index in range(len(count_list)): # 获取count_list下标
for j in range(count_list[index]): # 利用count_list对应下标里面的值实现多次添加
res.append(index) # count_list的index是array_list里面的值
# 完成时候,将排好的元素打印输出
return res
count_list_res = count_sort(array_list)
print(count_list_res)
三、优化思路
以上的内容是最简单的一种处理方法,当我们的数列当中,数字范围不是特别大的时候,我们可以采取上面的方法。
但是会有极端情况,比如这个列表里面的元素值都比较大
[94, 95, 92, 95, 94, 99, 98, 96]
虽然说是可以完成排序的,但是由于我们创建列表的个数是按照最大值+1来去创建的那么在创建完成之后,我们会发现,在整个列表的前90个空间都是没有被利用而浪费了的,这样的处理方式是不合理的
那么根据这样的一个问题我们可以这么去优化:
我们再创建列表的时候,长度可以参考最大值-最小值+1这样的方式去处理,这样的话我们就不会有那么多的空间被浪费了
那么代码可以做一个简单的修改:
res = []
def count_sort(array_list):
# 找出最大最小值,确定count_list的列表范围
max_value = array_list[0]
min_value = array_list[0]
for i in range(len(array_list)):
if array_list[i] > max_value:
max_value = array_list[i]
if array_list[i] < min_value:
min_value = array_list[i]
# 创建实际所用范围的列表空间
count_list = [0] * (max_value - min_value + 1)
print("创建实际所用规模的列表:{}".format(count_list))
# 接下来的思路就是
# 每一个array_list当中的元素,在count_list当中的下标
# 是具体元素减去最小值,得出一个差值作为偏移量
for i in range(len(array_list)):
count_list[array_list[i] - min_value] +=1
print("确定count_list:{}".format(count_list))
# 偏移量解决完之后,按照之前的思路逐一进行打印即可
for index in range(len(count_list)): # 遍历count_list
for i in range(count_list[index]): # 根据count_list中的值控制添加次数
res.append(index + min_value) # 下标就是偏移量,偏移量+最小值还原最终数据
return res
array_list = [94, 95, 92, 95, 94, 99, 98, 96]
print("原始列表:{}".format(array_list))
count_list_res = count_sort(array_list)
print("计数排序之后的结果:{}".format(count_list_res))
结果如下:
这样,将根据最大最小值的差值去构造列表的话,就避免了因为元素太大而导致存储空间的大量浪费
但是即便这样处理之后好像已经没什么问题了,但是还是有局限性
一方面,如果在一组数列当中,最大值和最小值的差值很大,那计数排序是不适合的
另一方面,如果数列当中不完全是整数,这应该怎么办,甚至全部都是浮点数这又该怎么办,那我们使用计数排序实际上是用不了的
这种问题,我们应该如何处理,那就需要考虑使用另外一种算法了,下一篇文章详细说明