为什么这个快速排序有效?
我发现这种快速排序分区方法令人困惑且错误,但它似乎有效。我指的是这个伪代码。 注意:他们在文章末尾也有一个 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 数组。
我还尝试使用此分区函数进行选择算法,但它未能产生正确的结果。它似乎有效,而且作为快速排序算法的一部分,它甚至非常快。
所以我的问题是:
- 任何人都可以发布一个该算法不起作用的示例吗?
- 如果没有,为什么它可以工作,因为分区部分似乎是错误的?这是另一种我不知道的分区方法吗?
I find this Quicksort partitioning approach confusing and wrong, yet it seems to work. I am referring to this pseudocode. Note: they also have a C implementation at the end of the article, but it's very different from their pseudocode, so I don't care about that.
I have also written it in C like this, trying to stay true to the pseudocode as much as possible, even if that means doing some weird C stuff:
#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;
}
If you run this, you'll get the following output:
1 6 2 3 4 8 7 - 5
However, this is wrong, isn't it? Clearly a[5]
does not have all the values before it lower than it, since a[2] = 6 > a[5] = 4
. Not to mention that 7
is supposed to be the pivot (the initial a[p]
) and yet its position is both incorrect and lost.
The following partition algorithm is taken from 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;
}
And produces this output:
1 6 2 3 8 4 7 - 1
Which is a correct result in my opinion: the pivot (a[r] = a[7]
) has reached its final position.
However, if I use the initial partitioning function in the following algorithm:
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.
}
}
... it seems to be a correct sorting algorithm. I tested it out on a lot of random inputs, including all 0-1 arrays of length 20.
I have also tried using this partition function for a selection algorithm, in which it failed to produce correct results. It seems to work and it's even very fast as part of the quicksort algorithm however.
So my questions are:
- Can anyone post an example on which the algorithm DOESN'T work?
- If not, why does it work, since the partitioning part seems to be wrong? Is this another partitioning approach that I don't know about?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为分区是正确的。 7是枢轴。原始数组被划分为一个长度为 5 的子数组(包含小于或等于 7 的元素)和一个长度为 2 的子数组(包含大于或等于 7 的元素)。
I think the partitioning is correct. 7 is the pivot. The original array is partitioned into a sub array of length 5 containing elements less than or equal to 7 and a sub array of length 2, containing elements greater or equal to 7.
从上面延伸这里应该是这样的
Extending on from above here is what it should look like
来自 Wikipedia (我已经强调了我认为直接解决您问题的部分):
这可能就是你所缺少的吗?
From Wikipedia (I've emphasized the part that I think addresses your question directly):
Could that be what you were missing?
您对项目的索引和 iten 值感到困惑
看看您的标头
现在,如果我们将数组 a 上的数据类型更改为某种奇怪的数据类型,您将看到问题
您从 main 中调用该函数,请
参阅问题a[0]是a[0]中条目的值而不是索引值。
想象一下,代码中 a[0] 的值为 200,只需将第一项值更改为 200,您将收到运行时错误“尝试访问超出范围的内存”,因为如果您遵循
通过 a[0] = 200 作为值 r 传递到分区,然后跟踪分区内发生的情况。
要记住的是,这是分区标头中的排序例程,数组 a 中的列表可能与索引的类型不同。标头的 p 和 r 显然是引用索引位置的索引,而 a 是列表待排序。
因此,你的排序的主要起点是
你明白为什么吗?数组 a 从 a[0] ... a[items_in_array-1] 运行,
因此在上面的示例中,您已将 8 个值预加载到数组中,因此来自 main 的分区调用应该是
You are getting confused between the index of the item and the iten value
Look at your header
Now if we changed the data type on the array a to some weird data type you will see the problem
You call the function from within your main with
See the problem a[0] is the value of the entry in a[0] not an index value.
Imagine a[0] had the value 200 in your code simply change the first item value to 200 and you will get a runtime error "attempt to access memory out of range" because if you follow
thru a[0] = 200 that is passed into partition as value r then follow what happens inside partition.
The thing to remember is this is a sort routine in your partition header the list in array a may not be of the same type as the indexes .. p and r of your header are clearly indexes referring to an index position and a is the list to be sorted.
Thus your main start to a sort is
Do you see why? Array a runs from a[0] ... a[items_in_array-1]
So in your sample above you have preloaded 8 values into your array so your partition call from main should be