稳定,高效的排序?

时间:2008-09-22 03:23:40

标签: algorithm language-agnostic data-structures sorting

我正在尝试创建一个非常节省空间的不寻常的关联数组实现,我需要一个满足以下所有条件的排序算法:

  1. 稳定(不会更改具有相同键的元素的相对顺序。)
  2. 就地或几乎就地(O(log n)堆栈很好,但没有O(n)空间使用或堆分配。
  3. O(n log n)时间复杂度。
  4. 另请注意,要排序的数据结构是一个数组。

    很容易看出有一个基本的算法匹配这三个中的任何两个(插入排序匹配1和2,合并排序匹配1和3,堆排序匹配2和3),但我不能为我的生活找到符合所有这三个标准的任何东西。

13 个答案:

答案 0 :(得分:10)

合并排序可以写成就地我相信。这可能是最好的路线。

答案 1 :(得分:8)

注意:标准快速排序 O(n log n)!在最坏的情况下,它可能需要花费O(n ^ 2)的时间。问题是你可能会转向远离中位数的元素,这样你的递归调用就会非常不平衡。

有一种方法可以解决这个问题,即谨慎选择一个保证或至少非常可能接近中位数的中位数。令人惊讶的是,你实际上可以在线性时间内找到确切的中位数,虽然在你的情况下听起来你关心速度所以我不建议这样做。

我认为最实用的方法是实施稳定快速排序(很容易保持稳定),但在每一步使用 5个随机值的中位数作为支点 。这使得您不太可能进行慢速排序,并且稳定。

顺便说一下,合并排序可以就地完成,虽然在原位和稳定的情况下都很棘手。

答案 2 :(得分:3)

快速排序怎么样?

Exchange也可以这样做,你的条款可能更“稳定”,但快速排序更快。

答案 3 :(得分:3)

Wikipedia上有一个排序算法列表。它包括按执行时间,稳定性和分配进行分类。

你最好的选择可能是修改有效的不稳定排序以保持稳定,从而降低效率。

答案 4 :(得分:2)

存在一类稳定的就地合并算法,尽管它们是复杂且线性的,并且在O(n)中隐藏了相当高的常数。要了解详情,请查看this article, and its bibliography

编辑:合并阶段是线性的,因此mergesort是nlog_n。

答案 5 :(得分:2)

因为您的元素在数组中(而不是链接列表),所以您可以在数组索引本身中获得有关其原始顺序的一些信息。您可以通过编写排序和比较函数来充分利用这一点,以了解索引:

function cmp( ar, idx1, idx2 )
{
   // first compare elements as usual
   rc = (ar[idx1]<ar[idx2]) ? -1 : ( (ar[idx1]>ar[idx2]) ? 1 : 0 );

   // if the elements are identical, then compare their positions
   if( rc != 0 )
      rc = (idx1<idx2) ? -1 : ((idx1>idx2) ? 1 : 0);

   return rc; 
}

只要sort ON执行元素交换,此技术可用于使任何排序稳定。元素的索引会发生变化,但相同元素的相对顺序将保持不变,因此排序仍然很稳健。它不会像heapsort一样开箱即用,因为原始的堆积“会抛弃”相对排序,尽管你可以将这个想法改编成其他类型。

答案 6 :(得分:2)

只需在每条记录中添加一个序列字段,在排序前将其初始化为索引并将其用作排序键的最不重要部分,就可以使Quicksort变得相当简单。

这对所花费的时间略有不利影响,但不会影响算法的时间复杂度。它还为每条记录提供了最小的存储成本开销,但是在您获得非常大量的记录(并且最大化记录大小)时这很少会发生。

我已将此方法与C的{​​{1}}函数一起使用,以避免编写自己的函数。在调用qsort()之前,每个记录都添加了一个32位整数并填充了起始序列号。

然后比较功能检查了键序列(这保证没有重复的键),将快速排序变为稳定的。我记得它仍然优于我正在使用的数据集本身稳定的mergesort。

您的里程可能会有所不同,所以请记住:测量,不要猜测!

答案 7 :(得分:2)

通过在链表上执行Quicksort可以使其稳定。这要花费n来选择3个枢轴的随机或中位数,但是具有非常小的常数(列表遍历)。

通过拆分列表并确保对左侧列表进行排序,使得相同的值向左移动,右侧列表进行排序,使得相同的值向右,排序将保持稳定,无需额外的实际成本。此外,由于这涉及分配而不是交换,我认为速度实际上可能比数组上的快速排序略好,因为只有一次写入。

总之,列出所有项目并在列表中运行快速排序

答案 8 :(得分:1)

也许shell sort?如果我正确地回忆起我的数据结构过程,它往往是稳定的,但更糟糕的情况是O(n log ^ 2 n),尽管它对几乎排序的数据执行O(n)。它基于插入排序,因此可以进行排序。

答案 9 :(得分:1)

在您证明重要之前,不要过分担心O(n log n)。如果你能找到一个具有极低常数的O(n ^ 2)算法,那就去吧!

如果您的数据受到严格限制,则一般最坏情况不相关。

简而言之:进行一些测试。

答案 10 :(得分:1)

有一个很好的排序函数列表on wikipedia可以帮助你找到你想要的任何类型的排序函数。

例如,为了解决您的具体问题,看起来就像您想要的就地合并排序。

但是,您可能还想看看strand sort,它有一些非常有趣的属性。

答案 11 :(得分:1)

我已经实施了stable in-place quicksortstable in-place merge sort。合并排序有点快,并且保证在O(n * log(n)^ 2)中工作,但不能在quicksort中工作。两者都使用O(log(n))空间。

答案 12 :(得分:0)

也许我有点像车辙,但我喜欢手工编码的合并排序。它简单,稳定,而且表现良好。它所需的额外临时存储空间仅为N*sizeof(int),这也不算太糟糕。