快速排序

来源:互联网 发布:mysql教学视频 百度云 编辑:程序博客网 时间:2024/06/06 10:43

重在递归的理解

算法步骤:
(1):选取主元(数组中随机一个即可,为简化程序,以下选取数组开头为主元);
(2):小于等于主元的放左边,大于等于主元的放右边;
(3):分别对左边,右边递归,即重复(1)(2)步。

void QuickSort(int array[], int left, int right){    if (left >= right)        return;    int base = array[left];    int i = left;    int j = right;    while (i < j)  // 小于等于主元的放左边,大于等于主元的放右边    {        while (i < j && array[j] >= base)            j--;        while (i < j && array[i] <= base)            i++;        swap(array[i], array[j]);    }    swap(array[left], array[i]);    QuickSort(array, left, i - 1);  // 分别对左边,右边递归    QuickSort(array, i + 1, right);}

有必要对以上代码的细节方面作个说明:
(1):递归结束条件

if (left >= right)    return;

结束条件应该是大于等于,而不仅仅是等于。

考虑这样的情况:在一段区间中,取开头作为主元,很巧的是该区间除开头(即主元),其它所有的数都大于主元,于是分别对两边递归:

QuickSort(array, left, i - 1);QuickSort(array, i + 1, right);

第一个函数的后两个参数: left 与 i - 1 ,前者大,后者小。
(2):等于主元的数如何放置

while (i < j && array[j] >= base)    j--;while (i < j && array[i] <= base)    i++;

本文的代码对等于主元的数采取的策略是:保持原位不动。

我们试着改变下代码:

while (i < j && array[j] > base)  // 去掉等号    j--;while (i < j && array[i] <= base)    i++;

我们把等于主元的数全部移动到左边。这么做,程序依旧运行正常。

我们再试着改下代码:

while (i < j && array[j] > base)  // 去掉等号    j--;while (i < j && array[i] < base)  // 去掉等号    i++;

左边等于主元的数移到右边,右边等于主元的数移到左边。

这样的做法有点诡异,似乎除了多了无用的交换操作耗时外,并不会对程序造成其它的影响。

但是请考虑这样的情况:
这里写图片描述

3 作为主元,i 和 j 指向的位置如上图,接着交换两个位置的值,

while (i < j)  {    while (i < j && array[j] > base)  // 去掉等号        j--;    while (i < j && array[i] < base)  // 去掉等号        i++;    swap(array[i], array[j]);  // 交换两值}

程序会永远在这步进入死循环。

有的人提议:只要在 swap 后再加一句代码就行了嘛。如下:

while (i < j)  {    while (i < j && array[j] > base)  // 去掉等号        j--;    while (i < j && array[i] < base)  // 去掉等号        i++;    swap(array[i], array[j]);  // 交换两值    i++; j++;  // 新增的代码}swap(array[left], array[i]);

但这么做,又引出了新的麻烦。

试想,若 i 的下一个位置就是 j(即 i+1 等于 j),在 i++; j++; 后,while 语句停止,swap 语句交换了主元和位置 i 的值,问题来了,i 指向的值或许是大于主元的,这就造成了程序的逻辑错误。

(3):i 和 j 的操作顺序

while (i < j && array[j] >= base)    j--;while (i < j && array[i] <= base)    i++;

为何 j 在前,i 在后?

其实这与我们选取的主元位置有关。程序中我们以数组首开头作为主元。

while (i < j){    while (i < j && array[j] >= base)        j--;    while (i < j && array[i] <= base)        i++;    swap(array[i], array[j]);}swap(array[left], array[i]);

while 语句停止的条件正好是 i 等于 j,接下来交换主元与 array[i] ,值得我们注意的是, array[i] 被交换至左边,但它可以保证其值总是小于等于主元么?

简单推理之下,若我们让 j 先行,i 后走,就可以保证被交换前,其值总是小于等于主元的。反之,若误写了下面的代码,排序后的结果就会是错的。

while (i < j && array[i] <= base)  // i 在前    i++;while (i < j && array[j] >= base)  // j 在后    j--;
原创粉丝点击