我需要用qsort稳定排序数组。为了确保结果稳定,我在比较函数中添加了一个额外的条件:
int compare(const void *p1, const void *p2)
{
if(*(const Data*)p1 < *(const Data*)p2)
return -1;
if(*(const Data*)p2 < *(const Data*)p1)
return 1;
else
return p1<p2 ? -1 : 1;
}
如果qsort从不调用compare(p,p),这将有效。否则我需要使用更复杂的条件。问题是,qsort是否曾经使用重复指针调用compare(),还是总是比较不同的指针?
更新:
我使用Ideone C ++编译器检查了这个:https://ideone.com/l026kM
对于注释中的小例子{8,8,1,1},提供的qsort()实现不会改变指针的顺序,也不会为同一元素调用compare。这似乎是合理的,因为每次反向交换都会影响性能,因为它需要稍后进行交换。我将使用随机生成的数组和不同的编译器来测试它。
更新:
在Ideone上测试了100000个随机数组,重复键的份额至少为80%。结果是100%稳定的排序数组。这是链接:https://ideone.com/KOYbgJ
VC ++ Express 2008编译器失败稳定排序,因为指针的顺序已更改。这基本上说明了VC ++实现与GCC实现的不同之处在于它不保持指针顺序。
答案 0 :(得分:6)
隐式允许任何未明确禁止的内容;特别是,我记得调试版本中的VC ++在实际执行std::sort
之前,在相同的元素(以及其他健全性测试)中明确地测试了比较器。不知道它是否与qsort
相同,但允许它。
但最重要的是,您的比较器违反了qsort
规定的要求;特别是:
当相同的对象(由
size
个字节组成, 而不管它们在数组中的当前位置 )被多次传递给比较函数时,结果应相互一致。也就是说,对于qsort
,他们应该在数组上定义一个总排序,对于bsearch
,同一个对象应始终以相同的方式与密钥进行比较。
(C99,§7.20.5¶4,重点补充)
最后,正如 Daniel Langr 已经概述的那样,即使采用“原谅”的实施方式,这也不一定能达到你所追求的目标。
这就是说:扔掉这个kludge并使用一个真正稳定的排序算法,该库已经提供了它(std::stable_sort
)。
此外,由于std::stable_sort
避免赋值运算符,因此这件事的重点似乎是std::sort
和qsort
更快的排序:
对于便宜的比较器,BTWqsort
通常比std::sort
慢,因为它需要间接调用每个比较,而在std::sort
中,函数内联。 并且复制通常也较慢,因为std::sort
必须使用memcpy
(必须调用然后在运行时确定大小并相应地复制),同时内联赋值被内联(所以再次,如果你的元素很小,它会更便宜,否则,因为合成的分配类型的复制赋值/副本通常归结为memcpy
答案 1 :(得分:5)
如果qsort从不调用compare(p,p),这将有效。
不,这不能确保快速排序的稳定性。如果这样做会很好:)
考虑(8,8,1,1)相对于枢轴5的划分。首先,外部8与外部1交换,然后内部8与内部1交换.1和8s的顺序在之后改变分区时没有比较具有相同键的元素。
可以更明确地写出如下:
partition(8_left, 8_right, 1_left, 1_right) -> (1_right, 1_left, 8_right, 8_left)