【问题标题】:quick sort and merge sort [closed]快速排序和合并排序[关闭]
【发布时间】:2010-12-06 04:18:07
【问题描述】:

嘿,谁能告诉我每个的基本算法和每个的跟踪序列。我很困惑网上有很多方法,我真的不知道哪个是最简单/最聪明的。谢谢。

【问题讨论】:

  • “跟踪序列”是什么意思?你是什​​么意思“多种方式”?基本上只有一种称为“快速排序”的算法和一种称为“合并排序”的算法;您可能会发现对该算法的不同解释,但它仍然是相同的算法。细节上只有微小的变化;我们可以通过在描述中更笼统/模糊来解决这个问题。
  • 就像元素在每一步之后的划分和排序方式一样。或者只是每一步执行后的数组
  • 维基百科是一个很好的起点:QuicksortMergesort

标签: c++ algorithm


【解决方案1】:

从根本上说,快速排序是一种自上而下的方法,而归并排序是一种自下而上的方法。在快速排序中,我们选择一个“pivot”或“partition”值,并将列表分成两个“一半”(并不总是正好一半,但越接近一半越好)——那些小于枢轴的,而那些更大的。然后我们对这两部分进行递归,结果是它们被排序了。

一个痕迹:

2, 3, 4, 1  (select pivot 3)
2, 1 | 3, 4 (partition < and >= partition)
1, 2 | 3, 4 (recursively sort halves)
1, 2, 3, 4  (done)

在归并排序中,我们将列表分成两半(没有排序——所以它可以正好是一半),然后递归地对这两半进行排序。然后在向上的过程中,我们“合并”这两个列表(它们本身已排序但未分区)。一个痕迹:

2, 3, 4, 1
2, 3 | 4, 1 (cut in half)
2, 3 | 1, 4 (recursively sort halves)
1, 2, 3, 4  (merge; done)

注意轨迹之间的区别:在 QS 中,我们首先对列表进行分区,因此左侧列表中的任何项目都不会大于右侧列表中的任何项目,但列表本身是未排序的。在 MS 中,我们首先对列表进行排序,但在合并之前它们之间没有任何关系。

两者平均为 N log N,但性能细节各不相同。值得注意的是,快速排序可以就地完成,但其最大的缺陷是必须选择一个支点。选择一个糟糕的支点可能会导致不分成两半,这可能会导致最坏的 O(N^2) 性能。合并排序总是精确地分成两半。

【讨论】:

  • +1 应该注意的是,并非所有快速排序都分成两部分 - 一个常见的变体分为 &lt;=&gt; 段,这可以降低 N^2 性能的机会(例如,在预排序和相同输入元素的常见情况下)
  • 我对合并排序不了解的是,当您到达2,3|1,4 时,两半都已排序,但要“合并”它们,列表仍然需要交错排列……您可以不要只是将一个粘到另一个上。那部分是怎么做的?
  • @Ralph,正是由于这个原因,合并排序不能就地完成 - 您需要分配至少一半的完整数组(手动或在堆栈空间中)才能有一些'scratch space' 将中间结果放入 - 即发生的情况是你有 3 个数组 a=[2,3];b=[1,4];c=[]; 然后你循环并执行if (a[i]&lt;b[j]) then c[k++]=a[i++]; else c[k++]=b[j++]; 的效果,直到其中一半用完......
  • @Ralph 抱歉,我没有详细解释。 “合并”是一种 O(n) 算法,它采用两个排序列表(不一定彼此排序)并形成一个完整的排序列表。为此,请查看两个列表的第一个元素,并确定哪个更小,然后将其移动到输出列表(2, 31, 4 -- 选择 1),从输入列表。现在你有2, 3 | 4。现在重复(2, 3 vs 4 -- 选择 2)。重复(34 - 选择 3)。直到其中一个列表为空;然后将剩余的列表放在最后:1, 2, 3, 4.
  • 哦……对了!这就说得通了。我以前学过这个,但后来我忘了:) 在实践中很少需要编写自己的排序算法。
【解决方案2】:

我认为this website might help you

有一个 java 小程序可以呈现算法的可视化

【讨论】:

  • 是否有任何小程序也可以跟踪代码的执行?而不仅仅是正在发生的事情的图片?
【解决方案3】:

另外,一个好的随机分区几乎可以消除快速排序的最坏情况,所以它会是 O(nlgn)。

【讨论】:

  • 是的。选择分区是一个棘手的问题。有趣的是,ideal 分区是中间值(正好是一半),但我相信找到中间值需要 O(n)。因此,如果您要这样做,您可以获得 O(n log n) ,但这将非常昂贵,不值得。在实践中,正如@Herberto 建议的那样,选择一个随机枢轴是最好的方法,因为即使在实践中枢轴有点偏离,您也会得到摊销 O(n log n)。
【解决方案4】:

快速排序的不变性是每次向下递归,右侧总是大于等于枢轴并且至少等于左侧。

然而,归并排序并非如此。但是,在尾递归中,当进行合并时,保证该递归处的项目子列表是有序的。

【讨论】:

    【解决方案5】:

    快速排序在数组中心周围的某处选择一个“枢轴”点。然后它将所有小于枢轴的元素移动到数组的低部分,并将所有等于或高于枢轴的元素移动到数组的高部分。枢轴将进入中间并位于正确的位置。然后将在数组的低部分和高部分调用快速排序,但都不包括枢轴。当它下降到两个值时,它会根据需要翻转它们并返回。

    合并排序需要一个额外的数组来放入它的新值,从而消耗更多的内存。该算法将在数组的顶部和底部调用自身。当这最终下降到两个或一个元素时,它会在必要时翻转它们并返回。一旦两半单独排序,合并排序将选择两个数组中较小的第一个值并将其放置在另一个数组中,直到两个数组都没有剩余值为止。合并排序总是和快速排序一样快,但它会消耗更多的内存。

    【讨论】:

      猜你喜欢
      • 2014-01-08
      • 2021-06-06
      • 2010-10-04
      • 2021-01-19
      • 2012-08-19
      • 1970-01-01
      • 2015-06-28
      • 2015-05-26
      • 2013-02-25
      相关资源
      最近更新 更多