是否有in-place partitioning算法(Quicksort实现中使用的那种算法)不依赖于数组中存在的pivot元素?
换句话说,数组元素必须按以下顺序排列:
如果它恰好存在于数组中,它仍然必须返回pivot元素的索引(排序后),否则返回特殊值;这可以是索引的one's complement,其中可以插入元素以维护顺序(如Java标准binary search函数的返回值。)
我看到的实现需要将pivot元素的索引作为参数给出(或者总是在数组的开头。)不幸的是我事先并不知道数组中是否存在pivot(或者它在数组中的位置。)
修改(回复meriton的评论):我们还可以假设满足以下条件之一:
数组中可能存在重复值(包括数据透视值的重复项。)
答案 0 :(得分:1)
这是一个有趣的问题。您可以通过数组的单个顺序传递来完成。 C#中的代码示例,如下所示。它假定一个名为a
和pivot
值的整数数组。
// Skip initial items that are < pivot
int iInsert = 0;
while (iInsert < a.Length && a[iInsert] < pivot)
{
++iInsert;
}
// Skip items that are = pivot
int numPivot = 0;
while (iInsert < a.Length && a[iInsert] == pivot)
{
++iInsert;
++numPivot;
}
int iCurrent = iInsert;
// Items will be added AFTER iInsert.
// Note that iInsert can be -1.
--iInsert;
while (iCurrent < a.Length)
{
if (a[iCurrent] < pivot)
{
if (numPivot == 0)
{
++iInsert;
int temp = a[iInsert];
a[iInsert] = a[iCurrent];
a[iCurrent] = temp;
}
else
{
++iInsert;
int temp = a[iInsert];
a[iInsert - numPivot] = a[iCurrent];
a[iCurrent] = temp;
a[iInsert] = pivot;
}
}
else if (a[iCurrent] == pivot)
{
++iInsert;
int temp = a[iInsert];
a[iInsert] = pivot;
a[iCurrent] = temp;
++numPivot;
}
++iCurrent;
}
int firstPivot = iInsert - numPivot + 1;
可能有一些优化机会。
这种方法的有趣之处在于,您可以轻松地将其调整为从传入数据流构建。你不必知道有多少物品要来。只需使用可以动态调整大小的列表。当最后一个项目进入时,您的列表的顺序正确。
答案 1 :(得分:0)
你很幸运:上个月编码kata是为了实现快速排序。这就是我想出的:
/**
* Sorts the elements with indices i such that l <= i < r
*/
private static void qsort(int[] a, int left, int right) {
int l = left;
int r = right - 1;
if (l >= r) {
return;
}
int pivot = a[l];
l++;
for (;;) {
while (l <= r && a[l] <= pivot) l++;
while (a[r] > pivot && l < r) r--;
if (l < r) {
int t = a[l];
a[l] = a[r];
a[r] = t;
} else {
break;
}
}
l--;
a[left] = a[l];
a[l] = pivot;
qsort(a, left, l);
qsort(a, r, right);
}
如您所见,该算法仅使用数据透视图的原始位置来查找数据透视表的值,并将数据透视交换到分区之间的索引。
如果我们不知道枢轴存在,我们只需将值等于枢轴值等于值&lt;如果不是将三个组中的元素分区小于,等于和大于枢轴,我们将分成两个小于或等于pivot的组,并且大于pivot,并递归到每个分区上。这个解决方案是正确的。
然而,终止将不再得到保证:已知QuickSort会终止,因为每个递归步骤都使用比调用者更短的数组切片,并且已知数组切片更短,因为它们不包含pivot元素。对于您修改的算法,情况不再适用。实际上,很容易看出终止将取决于您的支点价值选择策略。
答案 2 :(得分:0)
另一种可能性是将方法拆分为两个,一个分区为[&lt; = pivot,&gt; pivot]和另一个将该结果的第一部分划分为[&lt; pivot,&gt; = pivot]。
public static int partitionLE(double[] a, int left, int right, double pivot) {
double x, y;
if (left >= right) return left;
for (;;) {
while ((x = a[left]) <= pivot) {
if (++ left >= right) return left;
}
while ((y = a[right-1]) > pivot) {
if (left >= -- right) return left;
}
if (left < right) {
a[left] = y;
a[right-1] = x;
} else {
return left;
}
}
}
public static int partitionLT(double[] a, int left, int right, double pivot) {
double x, y;
if (left >= right) return left;
for (;;) {
while ((x = a[left]) < pivot) {
if (++ left >= right) return left;
}
while ((y = a[right-1]) >= pivot) {
if (left >= -- right) return left;
}
if (left < right) {
a[left] = y;
a[right-1] = x;
} else {
return left;
}
}
}
public static int partition(double[] a, int left, int right, double pivot) {
int lastP = partitionLE(a, left, right, pivot);
int firstP = partitionLT(a, left, lastP, pivot);
if (firstP < lastP) {
return firstP;
} else {
return ~firstP;
}
}