我们正在课堂上介绍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){}
答案 0 :(得分:3)
首先了解快速排序,接下来是三个中位数。
执行快速排序:
步骤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 成比例的数字。
那么哪个是最好的选择?最佳数字是在完成分区操作后将在中间结束的项目。但是我们不知道那是什么项目,因为找到中间项目我们必须对所有项目进行排序,这就是我们首先想要做的事情。
因此,我们可以采取一些策略:
选择第一个,因为,呃,为什么不呢?
转到中间位置,因为可能由于某种原因,数组已经排序或接近排序,如果不是,它的选择并不比任何其他更糟糕。
随机选择一个。
选择第一个,中间一个和最后一个,然后选择这三个中位数,因为它至少是这三个选项中最好的。
选择阵列前三分之三的中位数,第二个三分之三的中位数,最后三分之三的中位数,然后最终得到中位数那三个中位数。
这些有不同的优点和缺点,但总的来说,每个选项在选择最佳支点时比以前更好,但代价是花费更多的时间和精力来选择这个支点。 (随机还有进一步的优势,可以打败有人故意尝试创建数据的情况,作为某种DoS攻击的一部分,你会遇到更糟糕的行为。)
我的作业指出,为了继续对数组进行分区,枢轴必须位于左右边界之间。
是。当我们将3
分类到正确的位置并对左侧进行排序时再次考虑上面:
1 2 3 4 8 9 5 7 6.
现在,我们需要对范围4 8 9 5 7 6
进行排序。 边界是3
和4
之间的线以及6
和数组末尾之间的线(或另一种查看它的方式) ,边界是4和6,但它是包含边界,包括那些项目)。因此,我们选择的是4
(第一个)6
(最后一个)以及9
或5
取决于我们是否向上或向下划分按2计算(我们可能会在整数除法中向下舍入,因此9
)。所有这些都在我们当前正在排序的分区的边界内。因此,我们的三分中位数是6
(或者如果我们进行了整理,我们已经去了5
)。
(顺便说一句,一个神奇的完美枢轴选择器,总是选择最好的枢轴,只会选择6
或7
,所以选择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.你可以想象我们得到的这个数组不是快速排序的好例子。