排序算法学习整理四(计数排序)
四、计数排序:
个人觉得吧,计数排序是最简单的排序算法,就是特别浪费空间,接下来,来点严谨一些的语言:
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
排序思路:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
这一本正经的话讲的真累人,其实就是多开一个数组,那个数出现过,新开的数组下标和这个数相等的位置就加1;
打个比方:
存在一个数组:1 5 9 4 2 0 0 3 1 4 0
在这里面 1出现了2次,那么另一个数组array[1]就自加两次,0出现了3次,那么array[0]就自加三次……
然后就从array中从0开始到n结束,不是array[i]不是0,就填入原数组;
讲完了思路下面我们来尝试一下写代码,
嗯,不对,在这之前有个很重要的事情需要说明,由于不知道数组最大值,所以计数排序需要额外定义非常大的数组,不用客气,没炸就行。
好,开始写代码:
首先,先定义一个很大的数组
#define N 100000 //将N定义为100000,也可以用const int N = 100000; int a[N]; //最好定义在函数外面,因为太大了容易把主函数空间撑爆。(不到万不得已,不要在函数外定义变量、数组等等,常量可以)
接着是计数
for (int i = 0; i < n; i++) { a[arr[i]]++; //这个就是刚刚打比方的地方,记录一个数出现了多少次; }
最后是排序
for (int i = 0, j = 0; i < N; i++) { while (a[i]) //位运算等价于a[i] > 0,利用了非0即为真的特性,不是0就一直循环 { arr[j] = i; //只要a[i]不为0就赋值给arr[j]; a[i]--; j++; //移到下一个位置 } }
下面是完整的代码
1 #define N 100000 2 int a[N]; 3 void sort(int *arr, int count) 4 { 5 for (int i = 0; i < count; i++) 6 { 7 a[arr[i]]++; 8 } 9 10 for (int i = 0, j = 0; i < N; i++) 11 { 12 while (a[i]) 13 { 14 arr[j] = i; 15 a[i]--; 16 j++; 17 } 18 } 19 }
想都不用想计数排序的局限性很大,不仅浪费时间,而且无法给小数排序,而且仅仅在一定范围内速度很快,一旦最大值太大就会非常慢,而且会导致程序崩溃。
下面这种实现方法思路比较奇特,但是是较为正统的技术排序实现方式
思路图大致如下:
代码实现如下:
void CountSorted(int length) { int times[MAXSIZE] = {0}; int res[MAXSIZE] = {0}; int max = 0; for (int i = 0; i < length; i++) { times[array[i]]++; if (max < array[i]) { max = array[i]; } } for (int i = 0; i <= max; i++) { times[i] += times[i - 1]; } for (int i = length - 1; i >= 0; i--) { res[times[array[i]] - 1] = array[i]; times[array[i]]--; } for (int i = 0; i < length; i++) { array[i] = res[i]; } }
大道五十,天衍四九,人遁其一!