我们先来看看这九种排序算法的时间复杂度、空间复杂度和稳定性:
看完了各个排序算法的理论性能后,再来看一看在实践上的性能。
在程序里,随机数的范围为0~49999999;进行10次外循环,每一趟外循环里生成一个随机数组,数组的大小逐趟递增({50000, 100000, 500000, 1000000, 5000000, 10000000, 10000000, 50000000, 100000000, 500000000});在内循环里利用各个排序算法的函数对外循环里生成的数组进行排序,内循环执行15趟,然后记录下每一趟里每个算法所花费的时间,算出这15趟的平均花费的时间(ms)。
最后运行程序,得出以下的结果:
其中,
qsort:C函数库自带的排序函数qsort;
quickSort:自己实现的快速排序;
countSort:自己实现的计数排序(没有考虑空间利用率和负数的版本);
heapSort:自己实现的堆排序;
shellSort:自己实现的希尔排序;
mergeSort:自己实现的二路归并排序;
radixSort:自己实现的链式基数排序;
insertSort2:优化后的插入排序;
selectSort:自己实现的选择排序;
bubbleSort2:自己实现的经过简单优化的冒泡排序。
由于冒泡、插入和选择排序在待排序元素数量增长的情况下呈平方关系增长,所以当待排序元素个数大于1000000后就去除了这三种排序算法的比较了。
很不幸,在数组大小为500000000的时候,程序被无情的Kill了,估计是计数排序消耗太多的内存了。
从以上结果可以看出随着待排序元素个数的增加,各个 排序算法所花费的时间也随之增加,但是各个算法在时间增加的幅度上却有着不小的差别:
由于,此次实验中都是用C语言自带的随机数产生器,所以基本上可以认为产生的数组是随机的,进而可以知道此次比较主要是针对各个排序算法在平均情况下的时间性能。
首先来看看第一张图片的那三个平均时间复杂度O(n2)的排序算法(冒泡排序、插入排序和选择排序),可以从第一张图片中看出当数组大小从50000增长到100000时(大小变为原来的2倍),排序所花费的时间变为原来的4倍(大约);但数组大小从50000增长到500000时(大小变为原来的10倍),排序所花费的时间变为原来的100倍!大小和时间的关系基本上是平方关系。
我们再来看看,那三个平均时间复杂度为O(nlgn)的排序算法(快速排序、归并排序和堆排序),其中对于快速排序和堆排序,当数组从10000000增长到100000000(大小便为原来的10倍),排序所花费的时间变为原来的15倍到16倍(大约);当数组从1000000增长到10000000时(大小变为原来的10倍),排序所花费的时间变为原来的15倍到16倍(大约);大小和时间的关系基本上是(1/2)n*lgn的关系。对于归并排序,当数组大小从1000000增长到10000000时(大小变为原来的10倍),排序所花费的时间变为原来的11.7倍;当数组大小从10000000增长到100000000时(大小变为原来的10倍),排序所花费的时间变为原来的11.2倍;大小和时间的关系基本上是(1/3)n*lgn的关系。
最后我们来看看,那两个平均时间复杂度为常数时间的算法(基数排序和计数排序),其中对于基数排序,排序时间的增长倍数和数组大小增长倍数基本相当,满足线性关系;对于计数排序,刚开始排序时间的增长比数组大小的增长要来的小一些,但是当数组大小增长到10000000以后排序时间的增长倍数和数组大小的增长倍数基本相当,满足线性关系。
我的测试方法可能会有一定的局限性,但是也能从这次测试中看出各个排序算法的实际性能了。
还有就是,原本是想用-O3的编译优化选项,可是用这种方法生成的文件在执行时会抛出异常,导致程序终止,现在还不知是什么原因.......
下面是我的电脑的基本配置信息:
电脑的基本配置