归并排序
要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。
自顶向下的归并排序
归并排序应用了分治的思想,如果它能将两个子数组排序,它就能够通过归并两个子数组来将整个数组排序。
命题F:对于长度为 N 的任意数组,自顶向下的归并排序需要 (1/2)*NlgN 到 NlgN 次比较。
命题G:对于长度为 N 的人艺术组,自顶向下的归并排序最多需要访问数组 6NlgN 次。
因为递归会使小规模问题中方法的调用过于频繁,使用插入排序处理小规模子数组(比如长度小于15)一般可以将归并排序的运行时间缩短 10%-15%。
如果 arr[mid] <= arr[mid+1],就认为数组已经是有序的,跳过 merge() 方法。这个改动不影响排序的递归调用,但是任意有序的子数组算法的运行时间就变为了线性的。
当把辅助数组 aux[] 声明为 merge() 方法的局部变量时,即使每次归并很小的数组,都需要创建新的数组,这样创建新数组将成为归并排序运行时间的主要部分。更好的解决方案是将 aux[] 变为 sort() 方法的局部变量,并将它作为参数传递给 merge() 方法。
自底向上的归并排序
自底向上的归并排序是,先归并那些微型数组,然后再成对归并得到的子数组,如此这般,直到将整个数组归并到一起。
命题H:对于长度为 N 的任意数组,自底向上的归并排序需要 (1/2)NlgN 至 NlgN 次比较,最多访问数组 6NlgN 次。
自底向上的归并排序比较适合用链表组织的数据,只需要重新组织链表的链接就能将链表原地排序。
快速排序
快速排序切分方法的内循环会用一个递增的索引将数组元素与一个定值比较。归并排序和希尔排序一般较慢的原因是,它们在内循环中移动数据。
快速排序另一个优势是它的比较次数很少。
快速排序的最好情况是每次都正好能将数组对半分。
命题K:将长度为 N 的无重复数组排序,快速排序平均需要 ~2NlnN 次比较(以及1/6的交换)。
命题L:快速排序最多需要约 N^2/2 次比较,但随即打乱数组能够预防这种情况。
在快速排序中,对于小数组,使用插入排序,在 5~15之间的人一直在大多数情况下都让人满意。
对于包含大量重复元素的数组,三向切分快速排序的排序时间从线性对数降低到了线性时间。