Algorithm 为什么Quicksort的深层函数调用堆栈在输入中有许多重复项?

Algorithm 为什么Quicksort的深层函数调用堆栈在输入中有许多重复项?,algorithm,quicksort,Algorithm,Quicksort,我正在阅读节目采访的内容,在第43页,有以下行/引语: quicksort实现简单,运行时间长,函数调用堆栈深 在具有多个重复项的阵列上,因为子阵列的大小可能差异很大 一种解决方案是对数组进行重新排序,使所有小于轴的元素首先出现, 后跟等于轴的元素,后跟大于轴的元素 这被称为荷兰国旗分割 我在想象第一行中描述的问题时遇到了很多困难,因此我不明白荷兰国旗分割方案有什么帮助。我找不到一个网页能清楚地解释这一点。我能得到一些直观的解释吗?快速排序算法的步骤之一是,需要一些算法根据选定的轴对数据进行分区

我正在阅读节目采访的内容,在第43页,有以下行/引语:

quicksort实现简单,运行时间长,函数调用堆栈深 在具有多个重复项的阵列上,因为子阵列的大小可能差异很大

一种解决方案是对数组进行重新排序,使所有小于轴的元素首先出现, 后跟等于轴的元素,后跟大于轴的元素

这被称为荷兰国旗分割


我在想象第一行中描述的问题时遇到了很多困难,因此我不明白荷兰国旗分割方案有什么帮助。我找不到一个网页能清楚地解释这一点。我能得到一些直观的解释吗?

快速排序算法的步骤之一是,需要一些算法根据选定的轴对数据进行分区。理想的情况是每个分区都尽可能小,因此需要更少的进一步分区。如果超过一半的元素在一个分区中结束,则需要更多的步骤来完成该算法

作为一个说明性的例子,为什么重复导致了这种不对称性,考虑排序下面的列表,其中包含八个值相同的值中的六个元素:

2,2,1,2,2,2,2,3

如果您被要求将其划分为两个列表,那么您可能会将小于枢轴的所有元素放在一个分区中,而将大于或等于枢轴的所有元素放在另一个分区中

例如,这就是公共的工作方式,枢轴本身被排除在两个分区之外。这种算法通常被认为是相对简单的理解和实现,因此可能是作者心目中的短语“天真的实现”

在该方案中,第一步可能按如下方式对列表进行分区:

  • 支点:2
  • 小于枢轴:1
  • 大于或等于枢轴:2,2,2,2,2,3
然后对第二个分区进行递归分区:

  • 支点:2
  • 小于透视:[空列表]
  • 大于或等于枢轴:2,2,2,2,3
步骤3:

  • 支点:2
  • 小于透视:[空列表]
  • 大于或等于枢轴:2,2,2,3
步骤4:

  • 支点:2
  • 小于透视:[空列表]
  • 大于或等于枢轴:2,2,3
步骤5:

  • 支点:2
  • 小于透视:[空列表]
  • 大于或等于枢轴:2,3
步骤6:

  • 支点:2
  • 小于透视:[空列表]
  • 大于或等于枢轴:3
这里递归可以最终停止。如果分区总是大小相等(分区为4、2和1),那么这比我们预期的3个步骤要糟糕得多。枢轴的选择并不重要,因为即使我们很快找到3的正确位置,我们仍然需要为列表中的每2个选择一个步骤


“fat-pivot”或“Dutch-flag”分区方案通过将与pivot相等的所有值分离到第三个分区来扩展上述内容。这不仅仅是平衡分区,结果是两个分区都更小

在我们的示例中,结果立即如下所示:

  • 支点:2
  • 等于枢轴:2,2,2,2,2,2
  • 小于枢轴:1
  • 超过1:3
由于新分区中的值都相等,因此不需要进一步排序。在我们的示例中,其余的分区每个都有一个元素,因此根本不需要执行任何递归。对于不太极端的情况,剩下来排序的两个分区都会更小,因此需要更少的后续步骤来排序

然而,由于分区算法的更大复杂性,这一分区步骤的成本将更高


其他分区方案有两个分区,但允许在任意一个分区中包含与枢轴相同的值。这意味着分区在每一步都可以均匀地调整大小,即使有重复的值(尽管它不能保证它们是相同的)。当发明快速排序时,它具有这个特性

在这种情况下,第一步可能会得出如下结果:

  • 左分区:2,2,1,2
  • 右分区:2,2,2,3
对于我们的极端示例,这比“胖轴心”的效率要低,但比一个非常大的分区和一个非常小的分区的效率要高得多

快速排序有大量的运行时间和深度函数调用堆栈 在具有多个重复项的数组上

这仅适用于Lomuto类型的分区方案,与1961年发布的原始Hoare分区方案相比,这是1984年首次提及的快速排序的变体。通常,Lomuto分区步骤会产生3个子分区:元素<枢轴,枢轴(单个元素),元素>=枢轴。在所有元素都相等的情况下,分区步骤会导致0个元素=pivot,在n-1元素分区上递归,每个递归级别只删除一个元素,这是时间复杂度为O(n^2)的快速排序的最坏情况

对于Hoare分区方案,随着重复数的增加,分区将产生更接近相等大小的子分区。在所有元素都相等的情况下,Hoare分区方案会产生理想的50%/50%分割,因为分区循环会导致工作左(wiki文章中的i)和右(wiki文章中的j)索引在中间相遇。将存在不必要的相等元素交换,但避免交换的检查通常会增加运行时间,而不仅仅是针对典型情况进行交换。通常,随着重复百分比的增加,索引在分区中间附近相遇的概率越大

做三方派对