http://www.cnblogs.com/cj723/archive/2011/04/28/2031345.html
希尔排序相当于直接插入排序的升级,它们同属于插入排序类,堆排序相当于简单选择排序的升级,它们同属于选择排序类。而快速排序其实就是我们前面认为最慢的冒泡排序的升级,它们都属于交换排序类。即它也是通过不断的比较和移动交换来实现排序的,只不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。
快速排序:
优化快速排序:
1):优化选取枢轴
固定选取某一个位置的关键字作为首个枢轴是非常不合理的做法。为此可以选择:三数取中法,对于较大的数组,选取九数取中法。
2):优化小数组时排序方案
如果数组比较小,快速排序反而不如直接插入排序来的更好(直接插入是简单排序中性能最好的),其原因在于快速排序用到了递归操作,在大量数据排序时,这点性能影响相对于它的整体算法优势而言是可以忽略的,但如果数组只有几个记录需要排序时,我们可以使用性能更好的直接插入排序。
3):优化递归操作
原本的代码:
/* 对顺序表L中的子序列L->r[low..high]作快速排序 */
void QSort(SqList *L,int low,int high)
{
int pivot;
if(low<high)
{
pivot=Partition(L,low,high); /* 将L->r[low..high]一分为二,算出枢轴值pivot */
QSort(L,low,pivot-1); /* 对低子表递归排序 */
QSort(L,pivot+1,high); /* 对高子表递归排序 */
}
}
-----------------优化之后的代码-----------------------------
/* 对顺序表L中的子序列L.r[low..high]作快速排序 */
void QSort1(SqList *L,int low,int high)
{
int pivot;
if((high-low)>MAX_LENGTH_INSERT_SORT)
{
while(low<high)
{
pivot=Partition1(L,low,high); /* L.r[low..high]一分为二,算出枢轴值pivot */
QSort1(L,low,pivot-1); /* 对低子表递归排序 */
low=pivot+1; /* 尾递归 */
}
}
else
InsertSort(L);
}
递归对性能是有一定影响的,QSort函数在其尾部有两次递归操作。如果待排序的序列划分极端不平衡,递归的深度将趋近于n,而不是平衡时的log
2
n,这就不仅仅是速度快慢的问题了。栈的大小是很有限的,每次递归调用都会耗费一定的栈空间,函数的参数越多,每次递归耗费的空间也越多。 当我们将if改成while后(见高亮代码部分),因为第一次递归以后,变量low就没有用处了,所以可以将pivot+1赋值给low,再循环后,来一次Partition(L,low,high),其效果等同于“QSort(L,pivot+1,high);”。结果相同,但因采用迭代而不是递归的方法可以缩减堆栈深度,从而提高了整体性能。
归并排序:
原理:假设初始序列有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到不小于n/2个长度为2或1的有序子序列;再两两归并,....,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
递归法实现:
时间复杂度:由完全二叉树的深度可知,整个归并排序需要进行趟,因此,总的时间复杂度为O(nlogn),所以总的时间复杂度为O(nlogn),而且这是归并排序算法中最 好、最坏、平均的时间性能。
空间复杂度:由于归并排序在归并过程中需要与原始记录序列同样数量的存储空间存放归并结果以及递归时深度为log2n的栈空间,因此空间复杂度为O(n+logn)。
稳定的排序算法;比较占用内存,但却效率高且稳定的算法。