【不看会后悔系列】排序之——快速排序优化(随机数key,三值取中,三路划分,自省排序)【最全三路划分讲解】

   日期:2024-12-26    作者:zhxthai 移动:http://ljhr2012.riyuangf.com/mobile/quote/42605.html


前面J桑讲了快排的思想(感兴趣的朋友可以去看看~

戳我戳我戳我~~~

今天我们来讲快排的优化。


快速排序性能的关键点分析

决定快排性能的关键点是每次单趟排序后,key 对数组的分割。如果每次选 key 基本二分居中,那么快排的递归树就是一棵均匀的满二叉树,性能最佳。然而,实践中虽然不可能每次都是二分居中,但性能依然可控。

但如果出现每次选到最小值或最大值,划分为 0 个和 N-1 的子问题时,时间复杂度为 O(N²)。在数组序列有序时,就会出现这样的问题。

仍需解决的场景

  1. 数组中有多个与 key 相等的值

    • 例如
    • 例如
  2. 数组中全是相同的值

    • 例如

这些场景会影响快排的性能,可能导致最坏情况的出现。


在快速排序中,基准元素的选择至关重要。相比将最左边元素作为基准,随机选择基准有几个明显优势

  1. 避免最坏情况

    • 如果数组有序或近乎有序,选择最左边元素作为基准会导致每次分割的子数组大小不均匀,造成递归深度达到最大,从而使时间复杂度退化到 O(n²)。
  2. 提升分割效果

    • 随机选择的基准元素更有可能让数组均匀分割,这样递归深度会更小,效率更高。
  3. 更强的适应性

    • 在处理重复元素较多的情况下,最左边的基准可能导致很多冗余操作。随机选择可以更灵活地处理这些情况,减少不必要的比较。

总的来说,使用随机数作为基准选择能让快速排序在各种情况下更稳定、更高效。

它的实现是(随机选到key与最左边元素交换,还是让key = 最左边元素

 

三值取中与随机选择基准

概念

  • 三值取中

    • 三值取中是一种优化快速排序基准选择的方法。它通过选取数组中的第一个元素、最后一个元素和中间元素,计算这三个数的中位数作为基准。这种方法能够有效减少极端情况的发生,从而提高排序性能。
  • 随机选择基准

    • 随机选择基准是另一种优化策略,它通过在数组中随机选取一个元素作为基准。这样可以打破数组的有序性规律,降低最坏情况的概率,保持良好的平均时间复杂度。
特性三值取中随机选择基准选择方式选取第一个、最后一个和中间元素,取中位数作为基准。随机选取数组中的一个元素作为基准。性能稳定性能有效减少极端情况,特别适合近乎有序的数组。随机性可能导致偶尔选择极端值,风险较大。处理重复元素的能力对重复元素的处理较好,避免了不必要的比较和交换。在有大量重复元素时,可能导致不均匀分割。实现复杂度需要额外的比较步骤来找到中位数,略显复杂。实现相对简洁,随机性引入不确定性。适用场景适合特定情况,如近乎有序或重复数据较多的数组。灵活性高,适用于各种输入情况。

总结

三值取中和随机选择基准各有优劣。三值取中通过选择中位数能够提高稳定性,特别是在处理特定情况时表现出色;而随机选择基准则在实现上更为灵活,适合于更广泛的数据集。在实际应用中,可以根据数据特征和具体需求选择合适的方法,以获得最佳的快速排序效果。

三值取中的代码实现是

 

前面两个方式是解决如何找key的问题,三路划分主要是用于处理有大量与Key值相同的元素时的情况。

为什么要用三路划分

三路划分的基本思想

三路划分的核心思想可以简单地总结为将数组分为三段

  • 小于基准值(key)的部分
  • 等于基准值(key)的部分
  • 大于基准值(key)的部分

这种划分方法结合了两种常见的划分策略:Hoare 的左右指针法和 Lomuto 的前后指针法。通过使用三个指针来管理这三个区域,使得算法在处理重复元素时能够高效进行。

算法步骤

  1. 选择基准

    • 默认情况下,选择最左边的元素作为基准(key)。
  2. 初始化指针

    • 指针指向数组的开始位置。
    • 指针指向数组的结束位置。
    • 指针从 开始,遍历整个数组。
  1. 遍历和划分

    • 当 指针遇到

      • 小于基准值的元素:与 指针指向的元素交换,同时 和 都向右移动一位。
      • 大于基准值的元素:与 指针指向的元素交换,并将 向左移动一位(此时, 不动,因为交换后,当前指向的元素还需要判断)。
      • 等于基准值的元素:直接移动 向右。
    • 继续这个过程,直到 超过 。

  2. 三路划分算法通过将数组分为小于、等于和大于基准的三部分,有效地解决了快速排序在处理重复元素时的性能问题。它的核心在于利用三个指针来高效管理这三部分,从而提高了算法的效率和稳定性。在面对大量相同元素时,三路划分算法可以显著减少不必要的比较和交换操作,使得排序过程更加高效。

    三路划分具体实现代码是

     
    

    综合上述三值取中及三路划分,我们就得到了一个近乎完美的排序方法,它可以解决99%排序问题~

     
    

    这里给大家一个测试代码,大家下去可以测试快排的效率

     
    

    introsort的快排,introsort是introspective sort采⽤了缩写,他的名字其实表达了他的实现思路,他的思路就是进行自我侦测和反省,快排递归深度太深(sgi stl中使⽤的是深度为2倍排序元素数量的对数值)那就说明在这种数据序列下,选key出现了问题,性能在快速退化,那么就不要再进⾏快排分割递归了,改换为堆排序进⾏排序。

    自省排序说白了就是数据小时用直接插入排序,数据多时用快排,深度大时改为堆排序,将我们前面学到的排序都结合起来了,我们直接来看代码~

     
    

    到这里我们快排的优化就讲完了,当然往后还有其他的优秀排序,但是我们这里的快排已经能打败90%的排序了,是一个近乎完美的排序,大家要和J桑一起多看多练哦~


特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。


举报收藏 0评论 0
0相关评论
相关最新动态
推荐最新动态
点击排行
{
网站首页  |  关于我们  |  联系方式  |  使用协议  |  隐私政策  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号