Quicksort w /"中位数为3"枢轴选择:了解流程

时间:2015-11-29 03:50:20

标签: c++ quicksort partitioning multiple-languages

我们正在课堂上介绍Quicksort(带数组)。我一直跑到墙上试图绕过他们希望我们的Quicksort任务如何使用"三个中位数"枢轴选择方法。我只需要对它如何运作的高级解释。我们的文字没有帮助,而且谷歌很难找到明确的解释。

到目前为止,我认为这是理解的:

"三个中位数"函数采用index 0(第一个),array_end_index(最后一个)和(index 0 + array_end_index)/2(中间)中的元素。计算具有这些3的中值的指数。返回相应的索引。

以下功能参数:

/* @param left
*       the left boundary for the subarray from which to find a pivot
* @param right
*       the right boundary for the subarray from which to find a pivot
* @return
*       the index of the pivot (middle index); -1 if provided with invalid input
*/
int QS::medianOfThree(int left, int right){}

然后,在"分区" function,索引与&#34返回的索引匹配的数字;中位数为3"函数充当枢轴。我的任务指出,为了继续对数组进行分区,数据透视必须位于之间左边界和右边界。问题是,我们的三个中位数是"函数返回三个索引之一:第一个,中间或最后一个索引。这三个指数中只有一个(中间)可能是"中间"任何东西。

以下功能参数:

/* @param left
*       the left boundary for the subarray to partition
* @param right
*       the right boundary for the subarray to partition
* @param pivotIndex
*       the index of the pivot in the subarray
* @return
*       the pivot's ending index after the partition completes; -1 if
*       provided with bad input
*/
int QS::partition(int left, int right, int pivotIndex){}

我误解了什么?

以下是各项功能的完整说明

/*
* sortAll()
*
* Sorts elements of the array.  After this function is called, every
* element in the array is less than or equal its successor.
*
* Does nothing if the array is empty.
*/
void QS::sortAll(){}

/*
* medianOfThree()
*
* The median of three pivot selection has two parts:
*
* 1) Calculates the middle index by averaging the given left and right indices:
*
* middle = (left + right)/2
*
* 2) Then bubble-sorts the values at the left, middle, and right indices.
*
* After this method is called, data[left] <= data[middle] <= data[right].
* The middle index will be returned.
*
* Returns -1 if the array is empty, if either of the given integers
* is out of bounds, or if the left index is not less than the right
* index.
*
* @param left
*       the left boundary for the subarray from which to find a pivot
* @param right
*       the right boundary for the subarray from which to find a pivot
* @return
*       the index of the pivot (middle index); -1 if provided with invalid input
*/
int QS::medianOfThree(int left, int right){}

/*
* Partitions a subarray around a pivot value selected according to
* median-of-three pivot selection.
*
* The values which are smaller than the pivot should be placed to the left
* of the pivot; the values which are larger than the pivot should be placed
* to the right of the pivot.
*
* Returns -1 if the array is null, if either of the given integers is out of
* bounds, or if the first integer is not less than the second integer, OR IF THE
* PIVOT IS NOT BETWEEN THE TWO BOUNDARIES.
*
* @param left
*       the left boundary for the subarray to partition
* @param right
*       the right boundary for the subarray to partition
* @param pivotIndex
*       the index of the pivot in the subarray
* @return
*       the pivot's ending index after the partition completes; -1 if
*       provided with bad input
*/
int QS::partition(int left, int right, int pivotIndex){}

3 个答案:

答案 0 :(得分:3)

首先了解快速排序,接下来是三个中位数。

执行快速排序:

  1. 从您正在排序的阵列中挑选一个项目(任何项目都会执行,但哪个项目最适合我们将返回)。
  2. 对数组进行重新排序,使所有小于您所选项目的项目都在数组之前,并且所有项目都大于它之后。
  3. 以递归方式对您选择的项目之前和之后的集合执行上述操作。
  4. 步骤2称为&#34;分区操作&#34;。考虑一下你是否有以下内容:

    3 2 8 4 1 9 5 7 6
    

    现在假设您选择了第一个数字作为您的枢轴元素(我们在步骤1中选择的那个)。在我们应用第2步之后,我们最终会得到类似的结果:

    2 1 3 4 8 9 5 7 6
    

    3现在位于正确的位置,并且每个元素都在它的正确位置。如果我们现在对左侧进行排序,我们最终得到:

    1 2 3 4 8 9 5 7 6.
    

    现在,让我们考虑一下右边的元素:

    4 8 9 5 7 6.
    

    如果我们选择4来接下来我们最终会改变,因为它处于正确的位置开始。要设置左边的元素是空的,所以这里无关。我们现在需要对集合进行排序:

    8 9 5 7 6.
    

    如果我们使用8作为我们的支点,我们最终会得到:

    5 7 6 8 9.
    

    现在右边的9只有一个元素,显然已经排序了。 5 7 6需要排序。如果我们转向5,我们最终会将其排除在外,我们只需将7 6排序为6 7

    现在,考虑到更广泛背景下的所有变化,我们最终得到的是:

    1 2 3 4 5 6 7 8 9.
    

    因此,再次总结一下,quicksort选择一个项目,围绕它移动元素,使它们都相对于那个项目正确定位,然后以剩余的两个集合递归地执行相同的操作,直到没有未分类的块,一切都已整理好了。

    当我说'#34;任何项目都会做&#34;时,让我回到那里我捏造的问题。虽然任何项目都可以,但您选择的项目会影响性能。如果幸运的话,最终会在与n log n成比例的多个操作中执行此操作,其中n是元素的数量。如果你只是相当幸运,它会是一个稍微大一点的数字,仍然与n log n成正比。如果你真的不走运,那么它就是一个与n 2 成比例的数字。

    那么哪个是最好的选择?最佳数字是在完成分区操作后将在中间结束的项目。但是我们不知道那是什么项目,因为找到中间项目我们必须对所有项目进行排序,这就是我们首先想要做的事情。

    因此,我们可以采取一些策略:

    1. 选择第一个,因为,呃,为什么不呢?

    2. 转到中间位置,因为可能由于某种原因,数组已经排序或接近排序,如果不是,它的选择并不比任何其他更糟糕。

    3. 随机选择一个。

    4. 选择第一个,中间一个和最后一个,然后选择这三个中位数,因为它至少是这三个选项中最好的。

    5. 选择阵列前三分之三的中位数,第二个三分之三的中位数,最后三分之三的中位数,然后最终得到中位数那三个中位数。

    6. 这些有不同的优点和缺点,但总的来说,每个选项在选择最佳支点时比以前更好,但代价是花费更多的时间和精力来选择这个支点。 (随机还有进一步的优势,可以打败有人故意尝试创建数据的情况,作为某种DoS攻击的一部分,你会遇到更糟糕的行为。)

        

      我的作业指出,为了继续对数组进行分区,枢轴必须位于左右边界之间。

      是。当我们将3分类到正确的位置并对左侧进行排序时再次考虑上面:

      1 2 3 4 8 9 5 7 6.
      

      现在,我们需要对范围4 8 9 5 7 6进行排序。 边界34之间的线以及6和数组末尾之间的线(或另一种查看它的方式) ,边界是4和6,但它是包含边界,包括那些项目)。因此,我们选择的是4(第一个)6(最后一个)以及95取决于我们是否向上或向下划分按2计算(我们可能会在整数除法中向下舍入,因此9)。所有这些都在我们当前正在排序的分区的边界内。因此,我们的三分中位数是6(或者如果我们进行了整理,我们已经去了5)。

      (顺便说一句,一个神奇的完美枢轴选择器,总是选择最好的枢轴,只会选择67,所以选择6在这里相当不错,不过仍有时候三个中位数不幸,并最终选择第三个更差的选项,或者甚至是3个相同元素中的任意选择,所有这些都更糟糕。它的可能性要小得多。比其他方法发生的事情。)

答案 1 :(得分:1)

medianOfThree的文档说:

* 2) Then bubble-sorts the values at the left, middle, and right indices.
*
* After this method is called, data[left] <= data[middle] <= data[right].
* The middle index will be returned.

所以你的描述与文档不符。您正在做的是对数据中的第一个,中间和最后一个元素就地进行排序,并始终返回中间索引。

因此,保证枢轴索引位于边界之间(除非中间位于边界内...)。

即便如此,旋转边界并没有错...

答案 2 :(得分:1)

计算“三个中值”是一种在数组中获取伪中值元素并使该索引等于您的分区的方法。它是一种粗略估计阵列中位数的简单方法,可以提高性能。

为什么这会有用?因为从理论上讲,您希望将此分区值设置为数组的真正中位数,因此当您对此数组执行快速排序时,数据透视表将平均分配此数组并启用快速排序的良好O(NlogN)排序时间给你。

示例:您的数组是:

[5,3,1,7,9]

三个中位数看5,1和9.中位数显然是5,所以这是我们要考虑的快速排序分区函数的数据透视值。你接下来要做的是将这个索引与last和get

交换
[9,3,1,7,5]

现在我们尝试在中间的左边使用小于5的所有值,在中间右边的所有值都大于5。我们现在得到

[1,3,7,9,5]

将最后一个元素(我们存储分区值的位置)与中间

交换
[1,3,5,9,7]

这就是使用3中间的想法。想象一下,如果我们的分区是1或9.你可以想象我们得到的这个数组不是快速排序的好例子。