今天无聊就打算把所有的排序算法都看一遍。。。
- 计数排序的时间复杂度是O(n),在算法导论中,用决策树模型中论证了,比较排序的情况为nlogn的复杂度。而计数排序的时间复杂度小于他的原因就是它不需要进行比较。
- 计数排序的原理就是根据原数组(A)里面最大的元素来一个与其一样大的数组(B),新开的数组(B)的第 i 个元素的值就是 i 在原数组(A)里面出现的次数.这样就可以根据新开的数组(B)来确定原数组(A)的排序。
我们先来看看排序部分的实现:(len是数组的长度)
void countSort(int *arrayL, int len) { int findMax = 0; for (int i = 0; i != len; i++) if (findMax < arrayL[i]) findMax = arrayL[i]; findMax += 1; //元素应该是从 1 开始,而数组下标是从 0 开始,所以加 1 int *sortArray = new int[findMax]; //开始设定每个元素出现的次数都为 0 for (int i = 0; i != findMax; i++) sortArray[i] = 0; //当对应的元素出现时,个数加 1 for (int i = 0; i != len; i++) ++sortArray[arrayL[i]]; //排序 int count = 0; for (int i = 0; i != len; i++) { for (int j = count; j != findMax; j++) { count++; // sortArray 中在 count 前面的元素已经遍历过了 if (sortArray[j] != 0) { //这里 for 循环是当出现元素个数不为 1 的情况时 for (int k = 0; k != sortArray[j]; k++) { arrayL[i++] = j; //将元素的值赋到原数组 } i--; //for 循环里面还会自增 break; } } } }
这部分的代码应该不难理解,因为基本都已经在代码注释说明了。这里原理就是,新开的数组的下标就是原数组的元素值。这样的话,当新开的数组某下标对应的值不为 0 时,代表该下标值在原数组出现过,而我们知道下标是有序的,那么就可以在遍历新建数组的时候有序地将有在原数组出现过的值赋给原数组。这样原数组就变得有序了。
但根据这个算法的实现我们可以看出,它并不适合当数组的元素值很大的时候。因为这样的话新开的数组空间很大,当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k),那么当K很大时,新开数组太大,既浪费空间,时间上也会有很大开销,因为虽然看上去是Θ(n + k),但当k很大时时间还是很长的。所以当需要排序的数组的元素值均在0~100之间时会比较适合。特别是当有很多元素重复的时候。另外,对于浮点数,字符串的排序他也无能为力。
其实,还有一个地方用这个算法的话会比较好,那就是在基数排序中比较每个位数的时候,因为每位数都是在0~9之间,用这个算法就很perfect了。。。