基于比较的排序最好的时间复杂度为O(N*lgN),证明如下:
每种基于比较的排序都能够使用决策树描述其排序过程,每个节点最多有2个子节点。
该决策树的树叶的最大值即为所有可能的排序结果之和,即N的阶乘N!。
决策树的高度h即为比较的次数,因为二叉树节点数最多为2^h,所以有N! <= 2^h,根据斯特林公式可知:
h >= lgN! >= lg(N/e)^N = N*lg(N/e) = N*lgN - N*lge
因此算法复杂度最好为:
O(N*lgN - N*lge) = O(N*lgN)
如果要追求效率更高的排序算法,比如线性排序,就要使用其他的非基于比较的排序算法。
本文用C实现了两种线性排序算法,分别为计数排序Counting Sort 和 基数排序 Radix Sort。这两种算法的实现要求排序元素为整数。
计数排序包括两步:计数和分配。首先对每个元素出现的次数进行计数,然后设置前缀数组得知每个元素在完成排序的数组中的位置,最后依照前缀数组进行元素分配。
可以证明,计数排序的时间复杂度为O(k+n),其中k为元素最大值,n为元素个数。
计数排序简单实现如下:
/* Counting Sort include two steps:
* Countint and Distribution.
*/
void countingSort(int arr[], size_t nmeb)
{
int i;
int max;
int countArr[max];
int prefixArr[max];
int holdArr[nmeb];
max = arr[0];
for (i = 1; i < nmeb; i++) {
if (max < arr[i])
max = arr[i];
}
/* initialize countint array */
for (i = 0; i < max; i++)
countArr[i] = 0;
/* step 1: counting */
for (i = 0; i < nmeb; i++)
countArr[arr[i]]++;
/* bulid prefix array */
prefixArr[0] = countArr[0];
for (i = 1; i < max; i++)
prefixArr[i] = countArr[i] + prefixArr[i - 1];
/* step 2: distribution */
for (i = nmeb - 1; i >= 0; i--) {
holdArr[prefixArr[arr[i]] - 1] = arr[i];
prefixArr[arr[i]]--;
}
/* copy array */
for (i = 0; i < nmeb; i++)
arr[i] = holdArr[i];
}
比如, 对 (132, 321, 123)进行排序,按照10进制进行分层。
按照个位排序: 321,132,123
按照十位排序: 321,123,132
按照百位排序: 123,132,321
排序完成。
然而,不能够仅仅按照10或者2作为位数进行分层,这会造成大量的分层,以至于要耗费多次计数排序。
假设数组的最大值为k,而且2^b>=k 且 2^(b-1)<=k,我们要算出最优的分层位数r(二进制)。
可知,时间的复杂度为所分层数b/r与计数排序时间复杂度O(n+k)的乘积,即
O(b/r*(n+k)) = O(b/r*(n+2^b))
通过求导或者其他方法可知r=lgn时,值最优。
按照上面所述,基数排序的实现如下:
void countingSortBits(int arr[], size_t nmeb, int bits, int digits)
{
int i, max;
max = 1 << bits;
int countArr[max];
int prefixArr[max];
int holdArr[nmeb];
int copyArr[nmeb];
/* copy array hold one digits of original array */
for (i = 0; i < nmeb; i++)
copyArr[i] = arr[i] % (1 << ((digits + 1) * bits)) / (1 << (digits * bits));
/* initialize countint array */
for (i = 0; i < max; i++)
countArr[i] = 0;
/* step 1: counting */
for (i = 0; i < nmeb; i++)
countArr[copyArr[i]]++;
/* bulid prefix array */
prefixArr[0] = countArr[0];
for (i = 1; i < max; i++)
prefixArr[i] = countArr[i] + prefixArr[i - 1];
/* step 2: distribution */
for (i = nmeb - 1; i >= 0; i--) {
holdArr[prefixArr[copyArr[i]] - 1] = arr[i];
prefixArr[copyArr[i]]--;
}
/* copy array */
for (i = 0; i < nmeb; i++)
arr[i] = holdArr[i];
}
int radixSort(int arr[], size_t nmeb)
{
int i, r, k, max;
max = arr[0];
for (i = 1; i < nmeb; i++) {
if (max < arr[i])
max = arr[i];
}
for (r = 1; (nmeb >> r) > 0; r++)
;
for (k = 1; (max >> k) > 0; k++)
;
for (i = 0; i * r < k; i++)
countingSortBits(arr, nmeb, r, i);
}
简单测试:
int main()
{
int i;
int arr[10] = {89767, 12389, 13289, 12833, 11289, 87263, 48928, 12932, 32674, 65323};
printf("Original:\n");
for (i = 0; i < 10; i++)
printf("%6d", arr[i]);
printf("\n");
radixSort(arr, 10);
printf("Sorted:\n");
for (i = 0; i < 10; i++)
printf("%6d", arr[i]);
printf("\n");
return 0;
}
执行结果如下:
roo@ubuntu:~$ ./a.out
Original:
89767 12389 13289 12833 11289 87263 48928 12932 32674 65323
Sorted:
11289 12389 12833 12932 13289 32674 48928 65323 87263 89767
来源:oschina
链接:https://my.oschina.net/u/1049845/blog/222238