以下Quicksort分区算法是否会产生稳定的排序(即它是否保持元素的相对位置具有相等的值):
partition(A,p,r)
{
x=A[r];
i=p-1;
for j=p to r-1
if(A[j]<=x)
i++;
exchange(A[i],A[j])
exchang(A[i+1],A[r]);
return i+1;
}
答案 0 :(得分:42)
有一种情况,您的分区算法将进行交换,这将改变相等值的顺序。这是一个图像,有助于演示您的就地分区算法的工作原理:
我们使用j索引遍历每个值,如果我们看到的值小于分区值,我们将它附加到浅灰色子阵列,方法是将其与紧靠右侧的元素交换 - 灰色的子阵列。浅灰色子阵列包含&lt; =分区值的所有元素。现在让我们来看看阶段(c)并考虑三个9位于白色区域开头的情况,然后是1.这就是说,我们将检查9的是否是&lt; =分区值。我们看看前9个并且看到它不是&lt; = 4,所以我们将它留在原地,然后向前迈进。我们看下接下来的9并看到它不是&lt; = 4,所以我们也把它留在原地,然后向前迈进。我们还留下了第三个9。现在我们看1并看到它小于分区,所以我们将它与前9交换。然后为了完成算法,我们将分区值与i + 1的值交换,这是第二个9。现在我们已经完成了分区算法,原来第三个的9现在是第一个。
答案 1 :(得分:20)
如果您愿意添加第二个密钥,则可以将任何排序转换为稳定排序。第二个键应该是指示原始顺序的东西,例如序列号。在比较函数中,如果第一个键相等,请使用第二个键。
答案 2 :(得分:10)
当类似元素的原始顺序不变时,排序是稳定的。你的算法不稳定,因为它交换了相同的元素。
如果没有,那么它仍然不稳定:
( 1, 5, 2, 5, 3 )
您有两个元素,排序键为“5”。如果由于某种原因比较元素#2(5)和#5(3),那么5将与3交换,从而违反了稳定排序的合同。这意味着仔细选择pivot元素无济于事,您还必须确保在分区之间复制元素永远不会交换原始顺序。
答案 3 :(得分:5)
您的代码看起来与wikipedia上给出的样本分区函数非常相似,这个函数不稳定,因此您的函数可能不稳定。至少你应该确保你的轴心点r指向等于A [r]的值数组中的最后一个位置。
你可以让quicksort稳定(我不同意Matthew Jones那里)但不是默认和最快(heh)形式。
Martin(请参阅评论)在链接列表上的快速排序是正确的,您可以在第一个元素作为数据透视头开始,并在下部和上部子列表的末尾使用追加值数组。但是,quicksort应该在一个简单的数组而不是链表上工作。快速排序的一个优点是内存占用少(因为一切都在适当的位置)。如果您正在使用链接列表,那么您已经为所有指向下一个值的指针等产生了内存开销,并且您正在交换那些而不是值。
答案 4 :(得分:3)
如果您需要稳定的O(n * log(n))排序,请使用mergesort。 (顺便说一下快速排序稳定的最好方法是选择随机值的中位数作为枢轴。但是,对于所有等效的元素,这并不稳定。)
答案 5 :(得分:2)
来自维基百科:
Quicksort是一种比较排序,并在 有效的实施,不是 稳定排序。
答案 6 :(得分:1)
解决此问题的一种方法是不将Last Element of array作为Key。快速排序是随机算法。
它的表现在很大程度上取决于Key的选择。虽然算法def说我们应该把last或first元素作为键,但实际上我们可以选择任何元素作为键。
所以我尝试了Median of 3方法,它表示采用数组的第一个,中间和最后一个元素。对它们进行排序,然后使用中间位置作为键。
例如,我的数组是{9,6,3,10,15}
。因此,通过排序第一个,中间和最后一个元素,它将是{3,6,9,10,15}
。现在使用9作为关键。所以将键移到最后它将是{3,6,15,10,9}
。
我们需要注意的是,如果9次不止一次,会发生什么。这是自我不止一次的关键。
在选择键作为中间索引之后的这种情况下,我们需要遍历键到右端之间的元素,如果找到任何元素相同的键,即如果在中间位置到末尾之间找到9,则将该键作为键。
现在在大于9的元素的区域中,即如果找到任何9,则j的循环将其与小于i的区域的元素区域交换。你的数组将是稳定排序的。
答案 7 :(得分:1)
快速排序不稳定。这是不稳定的情况。
5 5 4 8
以前5名为枢轴,我们将在第1次传球后跟随 -
4 5 5 8
正如您所看到的,已经更改了5的顺序。现在,如果我们继续进行排序,它将改变排序数组中5的顺序。