本文共 1900 字,大约阅读时间需要 6 分钟。
在掌握快速排序算法之前,我曾浏览过中文维基百科上关于快速排序算法的代码演示以及的文章:,但后者的代码演示与前者存在差别。通过两者分别对同一数组进行手动排序并比较,我认为前者的算法更为合理。
我们先自定义一个没有经过排序的数组,看一看快速排序算法是如何对数组中的各个元素进行排序的。13, 25, 6, 1, 7, 63, 96, 71, 49, 52, 30, 28, 84, 74, 93, 69, 40
这是一个数组。我们从中任选一个数作为基准数c,比如30。
在开始执行排序操作之前,我们需要声明两个分列数组两端的整型变量a和b(a、b的值等于停留位置的数值的索引值),作为控制比较和交换数值的指针。将指针a置于最左侧,指针b置于最右侧。其中,当指针a停留的数c,就向左移动一个单位。至于移动的条件为什么不包括等于基准数c的情形,其实很容易理解:当基准数是数组中最小的数或最大的数时,可能导致指针a或b产生越界行为。当a不再位于b的左侧时,此回合交换即结束。在啊哈磊的文章中,他率先移动的是右侧的指针。我们从移动左侧的指针开始。在这个数组中,13至7之间的数都小于30,故指针a第一次停留时的数是63;而84至40之间的数都大于30,故指针b第一次停留时的数是28。现在交换63和28。新的排列顺序如下(a、b指针的位置已标出,下同):13, 25, 6, 1, 7, 28, 96, 71, 49, 52, 30, 63, 84, 74, 93, 69, 40------------------a-----------------------b--------------------
由于a依然位于b的左侧,此过程继续进行。
a继续向右移动,停留在96的位置。b继续向左移动,停留在30(基准数)的位置。现在交换96和30。新的排列顺序如下:13, 25, 6, 1, 7, 28, 30, 71, 49, 52, 96, 63, 84, 74, 93, 69, 40----------------------a---------------b------------------------
a继续向右移动,停留在71的位置。b继续向左移动,停留在30的位置:
13, 25, 6, 1, 7, 28, 30, 71, 49, 52, 96, 63, 84, 74, 93, 69, 40----------------------b---a------------------------------------
虽然a、b停留位置的数都满足交换条件,但b停留的位置是a曾停留过的位置,即b位于a的左侧而不是右侧。故此回合结束。
不过啊哈磊在他的文章中并没有这样叙述,而是令两个指针重合并将停留的数与基准数交换。但你仍需要知道他的这一做法是为了帮助你更好地了解快速排序算法。而如果待排序数列中的基准数是一个众数,那么这种交换反而是没有意义的。实际上,无论是先移动指针a还是先移动指针b,只要允许指针a位于指针b的右侧,两者中先移动哪一个的最终结果都是殊途同归。重新观察a、b指针停留的位置。我们可以发现:a停留位置左侧的所有数必定不大于基准数;b停留位置右侧的所有数必定不小于基准数。这两个结论适用于快速排序的任何阶段。进一步地说,如果指针b在某次停留时的位置位于指针a的左侧,那么指针b必定紧邻指针a。而当指针b位于指针a的左侧时,就可以将这个数组一分为二:13, 25, 6, 1, 7, 28------------------b96, 71, 49, 52, 30, 63, 84, 74, 93, 69, 40-a----------------------------------------
而指针b则成为前一个数列的后端指针,指针a则成为后一个数列的前端指针。
在新的数列中设置新的基准数并重复执行上述排序步骤,直至最后的每个数列除了该数列的基准数以外没有任何其他数,就完成了对这个数组的排序。但是,有一种情况需要注意:当a和b停留的数值都是基准数时,必须令指针a再向右移动一个单位。这样才能保证分割的两个数列不包含重复的值(重复的数即基准数)。相比渐进式比较相邻两个数的冒泡排序算法,以跳跃式比较两个数的快速排序算法无疑具有冒泡排序算法不可比拟的优点。当然,前者与后者在最坏情况下的时间复杂度是相等的。不过,原始的快速排序算法不能保证相等的数的相对顺序不变,它们的顺序可能被打乱。如果需要保留这种相对位置,必须对算法进行优化。转载地址:http://syjaa.baihongyu.com/