Quicksort算法,需要一些小的澄清

时间:2018-04-18 18:53:40

标签: java arrays algorithm sorting quicksort

我正在研究基本的数组排序,并且正在努力完全理解它的逻辑。我理解递归,意味着将数组拆分为数据库每一侧的两个数组,然后继续对每个子数组进行分区,直到达到只有一个元素的数组。 我并不总是完全理解的是while循环本身的实现。

http://www.vogella.com/tutorials/JavaAlgorithmsQuicksort/article.html

这里我遇到了一个选择中间元素作为数据透视的实现。我理解枢轴可以是任何元素,无论是第一个,最后一个,任何随机元素还是为最大效率而专门选择的元素。 由于某种原因,将中间元素作为枢轴,我发现它更直观易懂。

  while (i <= j) {
        // If the current value from the left list is smaller than the pivot
        // element then get the next element from the left list
        while (numbers[i] < pivot) {
            i++;
        }
        // If the current value from the right list is larger than the pivot
        // element then get the next element from the right list
        while (numbers[j] > pivot) {
            j--;
        }

        // If we have found a value in the left list which is larger than
        // the pivot element and if we have found a value in the right list
        // which is smaller than the pivot element then we exchange the
        // values.
        // As we are done we can increase i and j
        if (i <= j) {
            exchange(i, j);
            i++;
            j--;
        }
    }

这是相关部分。

1)为什么只有当i元素小于pivot或j元素大于pivot时,我们才增加i并减少j?为什么不平等呢? 如果一个元素等于枢轴,那么它就好了,因为它最终位于枢轴的哪一侧并不重要,那么为什么我们不能继续增加/减少呢? 我试过这样做,因为我预计最终结果没有排序,即使经过一步一步的调试,我也不明白出了什么问题。

2)我是否正确地说,除了边缘情况,在退出外部循环时,i = j +1?总是?而这些元素中的一个,i或j,是否具有我们使用的支点的价值?但是哪一个和为什么?

// Recursion
    if (low < j)
        quicksort(low, j);
    if (i < high)
        quicksort(i, high);

3)我在这里期待的是,而不是传递低到j和我到高, 是: 假设pivotIndex是循环结束后的pivot的索引,

quicksort(low, pivotIndex - 1);
quicksort(pivotIndex + 1, high);

因为pivotIndex正好在枢轴所在的位置,所以这是他的最终位置。 所以我也很乐意为此做出解释。

谢谢。

3 个答案:

答案 0 :(得分:1)

  1. 让我们一步一步地考虑你的建议:
    基本条件:
    arr = {1, 20, 3, 4, 20, 70, 80, 90, 100}
    i->1 j->100 pivot = 20 (the 2nd one)
    迭代1:
    while (numbers[i] <= pivot)... =&gt; i->70
    while (numbers[j] >= pivot)... =&gt; j->20 (2nd one)
    if (i <= j)... =&gt; false
    迭代2:
    while (i <= j) =&gt;假
    所以这使得数组已经排序。 (这不是真的)。

    在当前算法中,在第一次迭代中,i -> 20 (1st one)j -> 20 (2nd one)发生交换。这里重要的不是交换本身,而是i在第一次迭代中没有变得大于j的事实
  2. 如果i=j声明中的if怎么办?这会同时执行i++j--两者之间的区别2.如果不发生这种情况,那么是i=j+1并且您无法判断它是否i { {1}}或j等于枢轴,因为它取决于数组中枢轴的位置。
  3. 这正是这里发生的事情。退出外部循环后,i将等于pivotIndex + 1j将等于pivotIndex - 1; ij也将包含pivotIndex本身。您可以使用笔和纸一步一步地完成它,看看发生了什么。
  4. 希望有所帮助

答案 1 :(得分:1)

在进入角落的情况之前,让我们来看看算法的正常运行。样本起始数组如下所示。数据透视值为33.索引ij显示在数组上方。

enter image description here

while圈次更新iji向前移动到大于枢轴的第一个值。绿色(25和8)中的值小于枢轴,并且处于最终位置。红色(49)中的值大于枢轴,并且处于最终位置。

enter image description here

现在,数字51和14被交换,i递增,j递减。

enter image description here

while圈次更新ij。请注意,j没有移动。

enter image description here

交换后(枢轴值显示为黄色)

enter image description here

while次循环和i再次移过j,完成了分区。您可以看到从i到数组末尾的所有值都是红色(大于枢轴)或黄色(枢轴)。从j到数组开头的所有值都是绿色(小于数据透视)或黄色。

enter image description here

所以在这种情况下i=j+1和其中一个索引(i)指向枢轴。

现在让我们看一下i最终不等于j+1的数组,ij都不指向枢轴值。起始数组(再次使用33作为枢轴):

enter image description here

while循环之后:

enter image description here

用50交换2后

enter image description here

while循环之后:

enter image description here

请注意,ij都停在了透视值处。所以在交换之后,情况看起来像这样:

enter image description here

我相信回答问题2和3. i=j+1并不总是如此,并且其中一个索引指向枢轴并不总是正确的。此外,在某些情况下,算法已经从递归调用中排除了数据透视。

可以修改算法以在更多情况下排除枢轴,但这似乎有风险和混乱。除非阵列尺寸相当小,否则没有多大好处。在生产qsort实现中,当数组大小变小时,算法会切换到不同的方法(例如选择排序)。

问题1.如果修改了while循环以允许等于pivot的值,会发生什么?答案是其中一个索引可以在数组末尾运行。这是一个例子(使用33作为支点):

enter image description here

所有值都大于或等于数据透视表,因此j索引将在数组末尾运行:

enter image description here

取决于数组前面的内存中的内容,j索引可能会持续很长时间,甚至会导致程序崩溃。这可以通过比较j和零来解决 while(j>0 && numbers[j] >= pivot)但额外检查会减慢算法速度。不停留在数据透视表中节省的时间量与在while循环中进行额外检查所浪费的时间差不多。

答案 2 :(得分:0)

  1. 如果在第一个循环中包含相等性,则如果没有元素大于pivot,则不会停止。使用严格的不等式是确保算法停止的简单方法。