O(n) 级的排序算法存在已久,但他们只能用于特定的场景
一、计数排序
计数排序限制:
举个例子,我们需要对一列数组排序,这个数组中每个元素都是 [1, 9]区间内的整数。那么我们可以构建一个长度为 9 的数组用于计数,计数数组的下标分别对应区间内的 9 个整数。然后遍历待排序的数组,将区间内每个整数出现的次数统计到计数数组中对应下标的位置。最后遍历计数数组,将每个元素输出,输出的次数就是对应位置记录的次数。
计数排序限制:数据需要分布在一定的范围内
1、算法步骤
举个例子,班上有 1010 名同学:他们的考试成绩分别是:7, 8, 9, 7, 6, 7, 6, 8, 6, 67,8,9,7,6,7,6,8,6,6,他们需要按照成绩从低到高坐到 0~90~9 共 1010 个位置上。
用计数排序完成这一过程需要以下几步:
- 第一步仍然是计数,统计出:44 名同学考了 66 分,33 名同学考了 77 分,22 名同学考了 88 分,1 名同学考了 99 分;
- 然后从头遍历数组:第一名同学考了 77 分,共有 44 个人比他分数低,所以第一名同学坐在 44 号位置(也就是第 55 个位置);
- 第二名同学考了 88 分,共有 77 个人(44 + 33)比他分数低,所以第二名同学坐在 77 号位置;
- 第三名同学考了 99 分,共有 99 个人(44 + 33 + 22)比他分数低,所以第三名同学坐在 99 号位置;
- 第四名同学考了 77 分,共有 44 个人比他分数低,并且之前已经有一名考了 77 分的同学坐在了 44 号位置,所以第四名同学坐在 55 号位置。
- …依次完成整个排序
计数排序并不是把计数数组的下标直接作为结果输出,而是通过计数的结果,计算出每个元素在排序完成后的位置,然后将元素赋值到对应位置。这样排序后的元素仍是原来的对象,且排序算法是稳定的。
**如果将计数数组的下标直接作为结果输出,则排序后的元素不是原来的对象,算法也就没有稳定性可言。**在实际工作中,如果排序后的元素不是原来的对象,这样的排序算法几乎无法使用。因为被排序的对象往往都会携带其他的属性,但这份算法将被排序对象的其他属性都丢失了。
2、算法实现
vector<int> countSort(vector<int> arr){
/-----处理空数组情况-----/
if(arr.size() == 0) return arr;
/-----初始化数据范围----/
int maxNum = arr[0], minNum = arr[0]; //初始化最大最小值
vector<int> res(arr.size()); //初始化排序结果
for(int i = 1; i < arr.size(); i++){
//计算最大最小值
if(arr[i] > maxNum) maxNum = arr[i];
if(arr[i] < minNum) minNum = arr[i];
}
int rangeNum = maxNum - minNum + 1; //计算数据范围
/-----对数