摘抄自:https://www.cnblogs.com/developerY/p/3166462.html
https://blog.csdn.net/gaoruxue918/article/details/61467416
它是一个不需要比较的,类似于桶排序的线性时间排序算法。该算法是对已知数量范围的数组(元素必须是整数)进行排序。其时间复杂度为O(n),适用于小范围集合的排序。计数排序是用来排序0到100之间的数字的最好的算法。
基本思想:
对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。
为了更好的理解计数排序,我们先来想象一下如果一个数组里所有元素都是整数,而且都在0-k(这里的k可不是数组长度也不是数组下标,而是数组中最大的数值)以内。那对于数组里每个元素来说,如果我能知道数组里有多少项小于或等于该元素。我就能准确地给出该元素在排序后的数组的位置。
假设已知数组a = [1,0,3,1,0,1,1],我知道了a数组的长度为7,且里面有0,1,3三种数值,那么比3小的有6个,是不是就可以确定3应该排在最后呢?比1小的有两个,那么1是不是从第三位开始排列呢?
这时候我应该循环遍历一次分别得到0,1,3在a数组中出现的次数即2,4,1(0出现了两次,1出现了4次,2没有,3出现了1次),我可以创建一个数组c,长度为4,然后c[0] = 2, c[1] = 4, c[2] = 0, c[3] = 1
。这个数组c又是怎么回事呢?
其实c数组的下标 = a数组的元素(所以要求a数组元素都是整数)。但是由于a数组里面没有2却有3,那总不能让c数组没有下标2吧,所以c[2] = 0
但是c[3] = 1
且c的长度为4。
通过上面的例子我可以知道,c.length(c是需要给我用来过渡的数组) = k(k是需要排序的数组内最大的数值) + 1,那么a(a是需要排序的数组)里面的所有数值应该都在0~k之间,但是,a可能并不包含所有0~k之间的数值!那么a中没有的数值假设为x,c[x]应该等于0。
到了这一步,我已经看到了胜利的曙光!
接下来可以创建一个用于保存排序后的数组b,只需要对刚刚过渡的数组c进行遍历然后存数据即可。也就是b[0] = 0, b[1] = 0, b[2] = 1, b[3] = 1, b[4] = 1, b[5] = 1, b[6] = 3
。
自己动手实现一遍,看自己是不是真的能融会贯通:
但是,仔细看我的实现代码,用到了三个for循环,其中两个是嵌套的,这样效率并不算好吧。都说在小范围内使用计数排序效率高,可按我这样写怎么也跟高效率扯不上关系。而且算法思想说找到统计不大于数值的总数就能确定位置,我这好像有点驴头不对马嘴。那么该怎么做呢?直接贴出网上大神的代码吧。
网上的示例:
网上的代码中,第二个for循环我没看明白,然后我就打印了下C[i]
,C[i] = sum
之前打印出是2,0,2,3,0,1,分别对应0,1,2,3,4,5出现的次数;重新赋值后打印出来是2,2,4,7,7,8。sum
最终是8,到了第三个for循环,array[i]
是3,0,3,2,0,3,5,2,C[array[i]]
是72641583。
这里我想了半天,为什么要重新赋值?后来在 https://www.cnblogs.com/lustar/p/7308089.html 这里的注释找到了答案。这里修改C[i]
其实是为了统计不大于相应数值的总数。因为思想里说了,我要得到不大于某个数值的总数就能得到它在数组中的正确位置了。
既然理解了这里的赋值修改,那么修改后的C[0]
是2,代表不大于0的数有两个。以此类推。
那么第三个for循环内部的代码是什么意思呢?我将它剖开分析。
array[i]
其实就是原数组对应的元素同时也是C数组的对应下标!- 但是此时的
C[array[i]]
得到的值其实是不大于array[i]
的值的总数假设为x(包含array[i]
对应的个数)。那么是不是意味着array
数组中 <=array[i]
的元素一共有x个?不过我也不能保证array[i]
对应的值在数组中唯一且没有相等的!那么是不是意味着与array[i]
对应的值相等的数据中一定有一个排序后应该在数组中第x-1位(这里的x是个数,从1开始,而数组下标是从0开始的)?
如果数组中有其他与array[i]
对应的值相等,那么我该怎么确定这些相等的值排序顺序呢?
这里不得不佩服前辈们的思想了,用for循环从后向前遍历!!!为什么???因为
i
是从后向前遍历的,那么即使数组中存在多个值与array[i]
相等也不用慌,直接先排array[i]
就好了,毕竟即使几个值相等,我为了保证数组稳定性,那么在原数组中下标较大的自然在新数组中所有相同的值里面排在相对靠后位置。
那么接下来的C[array[i]]--
也应该很明白了。
我们想想,i
一开始是8-1=7
,array[7] == 3
,C[3]
代表不大于3
的总数一共有7个。而array
中除了array[7]
等于3外还有array[2]
和array[5]
的值都是3,那么是不是i
循环遍历到2和5时,都是C[3]
?可我在i == 7
时已经将array[7]
排好位置了,它不应该继续参与排序了,所以有相同值时,应该排好一个减去一个。
总结
最后,做个象征性的总结,当我们学习时,一定要手动去实践,有不懂得或者不是特别理解的就去实践,实践不仅是检验真理的唯一标准,还是提升技术的最强方法。