【排序算法】快速排序(四个版本以及两种优化)含动图)

   日期:2024-12-26    作者:miofuncn 移动:http://mip.riyuangf.com/mobile/quote/43410.html

制作不易,三连支持一下吧

【排序算法】快速排序(四个版本以及两种优化)含动图)

文章目录

  • 前言
  • 一.快速排序Hoare版本实现
  • 二.快速排序挖坑法版本实现
  • 三.快速排序前后指针版本实现
  • 四.快速排序的非递归版本实现
  • 五.两种优化
  • 总结


前两篇博客介绍了插入和选择排序,这篇博客我们将会介绍一个非常重要的排序算法——快速排序,它的实践价值要大远远于前两种排序。

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。


动图展示如下

这里再简单解释一下

我们一般选取最左或最后边的值作为基准值(key,如果我们选择最左边的值为key,且我们要排成升序序列,那我们就让最右边先走(为了保证相遇位置的值一定比key小,找到比key小的位置停下来,然后左边再走,找到比key大的位置停下来,两者交换,循环上述操作,直至left和right相遇,再将相遇位置的值与key交换,这样就完成了单趟排序。

key位置的元素就排到了合适的位置。 

之后递归左右区间即可。

代码实现

 

挖坑法是在hoare大佬版本的基础上为了好让大家理解而改进的一种思路,因为hoare版本如果以最左边的值做key,则必须要让右边先走,让最右边的值做key,则必须要让左边先走,否则会出现错误。 而如果是挖坑法,则无需关心这个问题。大家自然而然会按正确的方式实现。

动图演示如下: 

通俗一点解释

我们将key位置的值保存下来,将那个位置视为一个坑位,右边先走,找到比key小的值就填到坑位中,坑位就更新到新的位置,左边再找大,再更新坑位的位置,直到left和right相遇,将原来key中的值放到坑位中就完成了单趟排序。

这样我们就避免了考虑哪边先走的问题,因为如果左边为坑,我们自然而然的就会让右边先走

代码实现如下

 

前后指针版本是这三种方法里面最值得推荐的一种,因为它实现出来非常的简单。

动图演示如下

 

 简单解释一下

定义两个指针prev和cur,如果啊a[cur]比key位置的值小,就让prev前进一步,并交换cur和prev位置的值,如果a[cur]比key位置的值大,就只++cur。

最后,prev和cur之间的值就是整个序列中比key位置的值大的值。prev之前的值就是比key位置的值小的值。

代码实现如下: 

 
 
 

之前的三种方法虽然思路略有不同,但本质都是分治递归的思想,这种思想比较容易理解,但是递归深度过深可能会导致栈溢出,因此,我们必须掌握非递归的实现,以适用于所有场景。 

从递归改成非递归的方法一般有两种

  • 直接改成循环——例如:斐波那契数列
  • 借助于栈或队列的结构。

我们这里就是要借助于栈的数据结构。

整体思路就是压栈,每次取出一个区间进行单趟排序,然后再入栈,如果区间不存在或只有一个元素就不再入栈,直至栈为空时,排序就完成了。 

代码实现如下: 

 

 

从分治的角度看,快速排序是一种二叉树结构的排序,有些大佬觉得最后几层占了整个递归次数的百分之八九十,消耗有些大,所以当区间长度小于一定数量(一般是10)时就直接用直接插入排序就好了。

也就是说最小子问题不再是区间不存在或只有一个值了,而是区间长度小于10.

 
 

快速排序在序列有序时,效率是不高的,时间复杂度不再是O(N*logN,而是O(N^2)。

为了尽量避免出现最坏的情况,我们可以采用三数取中的方法,防止key是最大或最小的值。

 

 

以hoare版本为例,加入三数取中后,就是这样: 

 

 



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


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