要理解计数排序,就必须要了解什么是计数,以及为什么只适用于整数。
可以将排序问题拆分为一下两个子问题:
(1)如何确定原始数组内元素的排位次序并建立排序数组与其对应?
即101→1,103→5....
① 原始数组内元素是不连续的,那如何将不连续的元素与排序数组一一对应呢。这里通过 元素值-最小元素值=计数数组下标 的方法确定计数数组,计数数组下标为【0,max-len+1】。统计次数作为其元素值,不存在的元素值默认为0。
原始数组
计数数组
② 如图所示,计数数组的下标从原始数组的最小值开始算起,0=101-101,1表示101出现1次;2=103-101,1表示103出现3次。
如何根据出现次数确定排位次序呢?可以看到,计数数组的下标在创建之时便从小到大与原始数组对应,因此只要将出现的次数累加便可以得到按值大小比较的排位次序。
排序数组
(2)如何根据排序数组的位序重新排列原始数组内元素?
遵循两个原则即可
原则一:排序数组次序-1即为其在新数组的下标
例如,原始数组元素值101所对应的排序数组下标(101-101=0)处的位序值为1,表明其是第一小的元素值。1-1=0,就在新数组的下标0处赋值101。
原则二:为了处理数值相同位序相同的情况,在排列完一个元素值后需要把排序数组中其对应的位序-1
例如,原始数组103存在3个值,对应的排序数组下标(103-101=2)处的位序值为5。排列时对遇到的第一个103按照之前的方法,在新数组下标4处赋值103;而当遇到第二个103时,如果还是在下标4处赋值则会重复。直观来看,之所以位序值为5是因为数值相同而并列排序(1,2,5,5,5)。因此在对第一个103排列完成后可将位序值减一,下次排列时新数组的下标就变为(5-1-1=3),在下标3处赋值即可解决这一问题,保证不遗漏原始数组中的相同元素。
def countingSort(arr):
min_value = min(arr)
max_value = max(arr)
count = [0] * (max_value - min_value + 1) # 下标与范围内所有元素
for i in arr:
count[i - min_value] += 1 # 范围内所有元素出现的次数
for i in range(1, len(count)):
count[i] += count[i - 1] # 范围内所有元素的位序
result = [0] * len(arr) # 依照位序将原数组的元素重新排列
for i in arr:
result[count[i - min_value] - 1] = i
count[i - min_value] -= 1
return result