我发现这种Quicksort分区方法令人困惑和错误,但似乎有效。我指的是this pseudocode。 注意:他们在文章末尾也有一个C实现,但它与他们的伪代码非常不同,所以我不关心。
我也用C这样写它,试图尽可能地保持伪代码,即使这意味着做一些奇怪的C东西:
#include <stdio.h>
int partition(int a[], int p, int r)
{
int x = a[p];
int i = p - 1;
int j = r + 1;
while (1)
{
do
j = j - 1;
while (!(a[j] <= x));
do
i = i + 1;
while (!(a[i] >= x));
if (i < j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
else
{
for (i = 1; i <= a[0]; ++i)
printf("%d ", a[i]);
printf("- %d\n", j);
return j;
}
}
}
int main()
{
int a[100] = //{8, 6,10,13,15,8,3,2,12};
{7, 7, 6, 2, 3, 8, 4, 1};
partition(a, 1, a[0]);
return 0;
}
如果你运行它,你将得到以下输出:
但是,这是错的,不是吗?很明显,1 6 2 3 4 8 7 - 5
a[5]
自a[2] = 6 > a[5] = 4
以来没有低于它的所有值。更不用说7
应该是枢轴(最初的a[p]
),但它的位置既错误又丢失。
以下分区算法取自wikipedia:
int partition2(int a[], int p, int r)
{
int x = a[r];
int store = p;
for (int i = p; i < r; ++i)
{
if (a[i] <= x)
{
int t = a[i];
a[i] = a[store];
a[store] = t;
++store;
}
}
int t = a[r];
a[r] = a[store];
a[store] = t;
for (int i = 1; i <= a[0]; ++i)
printf("%d ", a[i]);
printf("- %d\n", store);
return store;
}
并产生此输出:
1 6 2 3 8 4 7 - 1
在我看来,这是正确的结果:枢轴(a[r] = a[7]
)已达到最终位置。
但是,如果我在以下算法中使用初始分区功能:
void Quicksort(int a[], int p, int r)
{
if (p < r)
{
int q = partition(a, p, r); // initial partitioning function
Quicksort(a, p, q);
Quicksort(a, q + 1, r); // I'm pretty sure q + r was a typo, it doesn't work with q + r.
}
}
......它似乎是一种正确的排序算法。我在很多随机输入上测试了它,包括长度为20的所有0-1数组。
我还尝试将此分区函数用于选择算法,但无法生成正确的结果。它似乎工作,但它甚至非常快,作为快速排序算法的一部分。
所以我的问题是:
答案 0 :(得分:4)
我认为分区是正确的。 7是枢轴。原始数组被分区为长度为5的子数组,其中包含小于或等于7的元素和一个长度为2的子数组,其中包含大于或等于7的元素。
答案 1 :(得分:1)
从上面展开它应该是什么样子
void swap(int *a, int *b)
{
int x;
x = *a;
*a = *b;
*b = x;
}
int partition(int s[], int l, int h)
{
int i;
int p;/* pivot element index */
int firsthigh;/* divider position for pivot element */
p = h;
firsthigh = l;
for (i = l; i < h; i++)
if(s[i] < s[p]) {
swap(&s[i], &s[firsthigh]);
firsthigh++;
}
swap(&s[p], &s[firsthigh]);
return(firsthigh);
}
void quicksort(int s[], int l, int h)
{
int p;/* index of partition */
if ((h - l) > 0) {
p = partition(s, l, h);
quicksort(s, l, p - 1);
quicksort(s, p + 1, h);
}
}
int main()
{
int a[100] = //{8, 6,10,13,15,8,3,2,12};
{7, 7, 6, 2, 3, 8, 4, 1};
quicksort(a, 0, 7);
return 0;
}
答案 2 :(得分:0)
来自Wikipedia(我已经强调了我认为直接解决你问题的部分):
这是就地分区 算法。它划分了部分 索引左和之间的数组 通过移动所有权利,包容性 小于或等于的元素 array [pivotIndex]到开头 这个子阵列,让所有更大的 跟随他们的元素。在里面 过程它也找到了最后的结果 枢轴元素的位置,哪个 它返回。 暂时移动了 枢轴元素到最后 子阵列,所以它没有进入 方式。因为它只使用 交流,最终名单也一样 元素作为原始列表。注意 元素可以交换 到达它之前多次 最后的地方。还应该指出 在枢轴重复的情况下 输入数组,它们可以传播 横跨左边的子阵列,可能在 随机顺序。这并不代表一个 分区失败,进一步 排序将重新定位,最后 将它们“胶”在一起。
这可能是你错过的吗?
答案 3 :(得分:0)
您对项目的索引和iten值
感到困惑查看标题
int partition(int a[], int p, int r) ;
现在,如果我们将数组a上的数据类型更改为某种奇怪的数据类型,您将看到问题
int partition( Otherdatatype a[], int p, int r) ;
您可以使用
从主要内部调用该功能partition(a, 1, a[0]);
见问题a [0]是[0]中条目的值而不是索引值。
想象一下,[0]代码中的值为200只是将第一项值更改为200,并且您将获得运行时错误“尝试访问超出范围的内存”,因为如果您关注 通过[0] = 200传递到分区作为值r然后跟随分区内发生的事情。
要记住的是,这是分区标题中的排序例程,数组a中的列表可能与索引的类型不同。标题的p和r显然是指向索引位置的索引和是要排序的列表。
因此,你的主要开始是
partition(a, 0, items_in_array-1);
你明白为什么吗?数组a从[0] ... a [items_in_array-1]
运行因此,在上面的示例中,您已将8个值预先加载到数组中,因此您对main的分区调用应为
partition(a, 0, 7);