新的排序方法?
之前在讲快排中提到了,在比较排序中时间复杂度最小也就是o(nlogn)了,那么人们就想到了能不能基于其他的原理找到时间复杂度更低的方法?
所以计数排序和基数排序就出现了。
这次先讲计数排序
先行思想
给你10个不一样的数字,要你排序,如果知道每一个数有多少比他小的,那么直接放在数组里面合适的位置不就行了?
那如果有相同的数字呢?
我们可以考虑找一个数组,下标从0到数字中能出现的最大值,将每一个数字存储起来,最后再看下标放回去不就行了?
例:1135542
下面两行的数组中我们可以读出来有两个1,一个2……
然后就是排序,因为知道有两个1,(应该考虑的是有没有,先不看有几个)我们拿出来一个,那么存储个数的数组就要减一,如果还有,拿到没了……最后原来的数组就是有序的了。
#include <stdio.h>
#include <stdlib.h>
void COUNT_SORT(int m[],int max_number,int length)
{
int number[max_number+1];//从0到最大项
for(int i = 0; i <= max_number; i++)//初始化计数器
number[i] = 0;
for(int j = 0; j < length; j++)//统计
{
number[m[j]]++;
}
/*
for(int i = 0; i <= max_number; i++)
printf("%d ",number[i]);
printf("\n");
*/
int j = 0;
for(int i = 0; i <= max_number; i++)//找位置放。
{
while(number[i])
{
m[j] = i;
j++;
number[i]--;
}
}
}
int main()
{
int m[7] = {1,1,3,5,5,4,2};
COUNT_SORT(m,5,7);//数组,最大项,长度
//第二个参数要给出数组的最大项
//(其实不给自己跑一遍也是O(n),也还好)
for(int i = 0; i < 7; i++)
{
printf("%d ",m[i]);
}
return 0;
}
注释部分为测试使用的。
分析
初始化统计数组,时间复杂度理论上和n还没有关系……
(其实这里有一个坑就是如果有的数特别大很容易导致数组很多数据为空,那么可以考虑一手线性链表解决问题。)
统计需要遍历一次,在放回的过程中也是对n个数据来处理,虽然是两层循环,但是加一起也就是O(n)
这里面如果不喜欢我给出的方式,可以考虑回放到另外一个数组,遍历m[i]找到合适的下标然后放在新数组里面,这样很明显能看出来是O(n)的。
有一个关键点就是在这时候只知道一个数据有几个是不行的,还要知道在哪个位置,所以我们要对数组每一个下标对应的数据加上前面的数据。
大约就是下面的样子:
//用下面的替换最后一个for循环
int another[length];
for(int i = 1; i <= max_number; i++)
number[i] += number[i-1];//求得每一个下标数据对应的位置
/*
//测试
for(int i = 0; i <= max_number; i++)
printf("%d ",number[i]);
printf("\n");
*/
for(int i = 0; i < length; i++)
{
another[number[m[i]]-1] = m[i];//从零开始,所以减一
number[m[i]]--;
}
//输出验证
for(int i = 0; i < length; i++)
printf("%d ",another[i]);
这个思想在之前三元组压缩稀疏矩阵的时候,我们转置三元组还用到了。
如果是后面的方式(也就是按照原数组的顺序来遍历),能够保证该排序算法是稳定的,因为相同数值的元素在移动到数组another的时候顺序不变。
另外虽然人家是o(n),但代价是两个数组,所以应用和快排相比还是不太好。