Jon Bentleys美丽的快速入门 - 它甚至如何运作?

时间:2012-04-06 21:41:22

标签: algorithm quicksort data-partitioning

我认为我对quicksort的工作方式有了很好的理解,直到我在http://code.google.com/edu/algorithms/index.html上看到Jon Bentley推出他的“漂亮的快速排序代码”的视频,如下所示:

void quicksort(int l, int u){
    int i, m;
    if(l >= u) return;
    m = l;
    for(i = l+1; i<= u; i++)
        if(x[i] < x[l])
            swap(++m, i); //this is where I'm lost. Why the heck does it preincrement?

    swap(l, m);

    quicksort(l, m-1);
    quicksort(m+1, u);

}

另一个令我困惑的算法是FOR循环后的最终交换。为什么这是必要的?让我们假设数组已经按顺序排列。如果这是真的,那么因为x [i]&gt;所以不会发生掉期。 X [1]。最后,我们用m交换l。这搞砸了订单。

我错过了什么吗?

感谢。

5 个答案:

答案 0 :(得分:5)

在开头m设置为l,元素x[l]被选为分区元素(pivot)。

接下来,算法迭代一个列表,只要它找到一个小于x[l]的元素,它就会在当前 m之后移动它。 这意味着,当m > l时,l+1m的所有位置上的元素都小于元素x[l]

例如:

3 5 4 2 1  l = 0, m = 0, i = 1
  ^
3 5 4 2 1  l = 0, m = 0, i = 2
    ^
3 2 4 5 1  l = 0, m = 1, i = 3
      ^
3 2 1 5 4  l = 0, m = 2, i = 4
        ^

最后,我们将最后一个较小的数字与第一个(分区)元素交换得到:

1 2 3 5 4

如果没有比第一个更小的元素,则交换不执行任何操作(因为m等于l)。

答案 1 :(得分:1)

x[l]处的元素是选定的轴。 for循环的不变量是所有元素x[l+1]x[m]都小于数据透视,而x[m]x[i]的所有元素都大于或等于数据透视。

当找到小于枢轴的元素时,它会将其向下移动到条目m+1,然后向上碰撞m。 (m+1处的条目大于枢轴,因此将其向上移动很好。)

最后一次交换是将枢轴从x[l]移动到x[m],因为它需要在下部阵列和上部阵列之间结束。如果没有发生交换(排序数组示例),则m==l和最终交换不会移动任何内容。

设置m = l + 1并使用m++代替++m,教学代码更清晰。

答案 2 :(得分:1)

分区算法很好且易于记忆。它已经在ACM的通讯中或由Benltey多次重述。它也出现在Bentley的Programming Pearls一书中。我们的想法是跟踪否定后置条件的元素,即支点背后的元素越小,指数越高。但是,如果选择随机元素不是随机的,我们可能会得到最大(或最小)的元素,这将使我们与O(n)进行更多的交换。 java中的实现和解释是[blog]:http://harisankar-krishnaswamy.blogspot.in/2013/05/quick-sort-partition-algorithm.html“here”

答案 3 :(得分:0)

如果数组已排序,则m永远不会从其初始值l更改,因此swap不执行任何操作。

答案 4 :(得分:0)

固定索引lu指向要排序的子数组的第一个和最后一个元素。始终选择值x[l]作为轴。

在循环的顶部,子数组(不包括枢轴x[l])划分如下:

  • 较低的区域:已经测试了索引l < index <= m的元素,发现它们是< x[l]
  • 中间区域:索引为m < index < i的元素已经过测试,发现为>= x[l]
  • 上部区域:索引为i <= index <= u的元素尚未经过测试

一个可能的混淆源是,在循环运行时,值大于枢轴的区域是中间部分,而不是上部部分。在未经测试的区域耗尽之前,它不会到达阵列的上部 - 通过重复交换有效地“移动”,以便为扩展的下部区域腾出空间。

具体地,每当下部区域需要扩展时,循环变量m被预先递增:中间区域的第一个元素(如果有的话)必须移动到末尾,以便扩展下部区域。请注意,如果中间区域为空,则在预增量之后m == i(对于这种情况,swap()操作必须是无操作)。

最后,将枢轴x[l]交换到下部区域的末尾,将其放置到递归步骤的位置。请注意,如果数组已按顺序排列,则下部区域为空m == l,最后swap()操作再次为无操作。