我们上一篇文章讲述了快速排序的一些要素,我给出代码中其实考虑了某些优化,我们接下来对快速排序中用到和么用到的优化都进行一定的讲解:
1)就地分割(Partitioning in place)
真的原谅一下我的语文表达能力,其实就是考虑分割过程中需不需要多余数组来辅助的问题。如果我们使用一个多余的数组来实现分割过程,可能会让代码编写更简单,但是也没有那么简单到需要我们浪费将复制的数组重新复制到原数组的地步,因为我们用的递归实现,每一次数组的复制过程都会极大的增加程序的运行时间。
2)保持在边界内(Stay in bound)
如果最大或者最小的元素是分割元素(partition element),我们就需要考虑指向不能跑出数组的左右边界。当然,我在前文的partition()程序中避免了这种情况,就是运用判断语句j==lo和i==hi。
3)保持随机(Preserve randomness)
我们通过前一篇文章知道了,在partition过程中我们首先做的事shuffle来降低遇见最坏情况的概率。但是,这样我们让两个子序列也变的随机序列,这样对我们预测运行时间有决定性的影响,因为子序列的的顺序可能和原序列中的元素顺序不一样了。并且,shuffle的运行时间在递归过程中也大大的影响了运行时间。这个时候,我们可以采用一个较好的方法,我们随机选择序列内的一个元素作为分割元素,然后进行quicksort过程,这样也能有效的避免遭遇最坏情况。
4)终止循环(Termination the loop)
有经验的程序员都会非常谨慎的确定这些循环都能够终止,和他终止的条件。这对编写partition方法的时候也同样重要。一个很常见的错误便是,很多程序员在设计partition的时候不考虑序列内可能包含有其他和分裂元素相同的元素。当然上篇文章我们使用compared是有所考虑的。这边可能造成循环不能终止的情况
5)处理和分割元素相同值的元素(Handling the items with keys equal to the patition item's keys)
最好,如果元素值大于或者等于分割元素的值的时候,我们停止左扫描;元素值小于或者等于分割元素的值的时候,我们停止右扫描;这就是如前文程序所示。即使这样会创建一些非必要的交换次数,但是对一些特殊的应用,我们则会大大降低平方的运行时间(quadratic running time)
6)终止递归(Termination the recursion)
好的程序员会在一开始使用递归的时候就考虑递归在何处开始向上返回。这是个很重要很重要也很基础的事情,不然我们的递归不能向上返回。
上面其实是一些很微妙的注意事项,虽然会产生较大的影响,但是对于这个算法的优化,我们在这里提出三点非常重要的优化选择:
上面这三点优化其实挺微妙的,因为对不同的数据请求有不同的数据规定,这些优化不一定满足其要求,但是还是能够提高快速排序的运行时间,所以这里我们给出原文,有兴趣的同学可以看一下,重要的东西是上篇文章中所提到的。