《经典排序算法-快速排序》

快速排序是实践中已知的最快的排序算法,平均运行时间O(NlogN),该算法之所以快是因为非常精炼和高度优化的内部循环。它也是一种分治的递归算法,将数组S排序的基本算法由下列简单的四步组成:

  1. 如果S中元素个数是0或1,则返回。
  2. 取S中任一元素v,称之为枢纽元。
  3. 将S-{v}分成两个不相交的集合S1={x∈S-{v}|x<=v}和S2={x∈S-{v}|x>=v}
  4. 返回quicksort(S1)后,继随v,继而quicksort(S2)。
    对于那些枢纽元的元素的处理,第三步分割的描述不是唯一的,因此这就成了一个设计上的决策,一部分好的实现方法是将这种情形尽可能有效的处理。下图解释了快排对一个数集的操作。
    在这里插入图片描述
    快速排序比归并更快的原因在于第三步分割成两组在适当的位置进行并且非常有效,它的高效弥补甚至超出了大小不等的递归调用。
    选取枢纽元
  5. 一种错误的方法
    通常没有充分考虑的选择是将第一个元素作为枢纽元(或者选取前两个互异关键字中较大者作为枢纽元),如果输入是预先排好序的或者反序的,因为所有的元素都不是s1,都被划入s2,此外第一个元素做枢纽元并且输入预先排好序,快排花费的时间将是二次的。
  6. 一种安全的做法
    安全的做法是随机选取枢纽元,但代价昂贵,根本减少不了算法其余部分的平均运行时间。
  7. 最好的选择
    三数中值分割法,一组N个数的中值是第N/2个最大的数,枢纽元最好的选择是数组的中值,一般做法是使用左端、右端、中心位置三个元素枢纽值作为枢纽元。
    分割策略
    有几种分割策略用于实践,但此处描述的分割方法能够给出好的结果,该法的第一步是通过将枢纽元与最后的元素交换使得枢纽元要离开被分割的数据段,i从第一个元素开始而j从倒数第二个元素开始。
    在这里插入图片描述
    分割阶段要做的就是把所有小元素移到数组的左边而把所有的大元素移到数组的右边。大小相对于枢纽元素而言。
    当i在j的左边时,将i右移,移过那些小于枢纽元的元素;并将j右移,移过那些大于枢纽元的元素,当i和j停止时,i指向一个大元素而j指向一个小元素,如果i在j的左边,那么这两个元素互换一下,其效果是把一个大元素移向右边而把小元素移向左边,下面为交换过程:
    在这里插入图片描述

交换i和j指向的元素直到i和j彼此交错为止。
在这里插入图片描述
分割最后一步是将枢纽元与i指向元素交换。
在这里插入图片描述
在最后一步当枢纽元与i所指向的元素交换时,我们知道在位置P<i的每一个元素都必然是小元素,在P>i上的元素都是大元素。
必须考虑的重要细节是如何处理等于枢纽元的关键字,先考虑数组中所有关键字都相等的情况,如果i和j都停止,在相等的元素间将会有多次交换,其正面意义是i和j在中间交错,分割建立了两个相等的子数组,因此总的运行时间是O(NlogN)。而如果i和j都不停止或者其中一个停止都会花费O(N2)的时间,因此让i,j停止效率更高。

对于很小的数组(N<=20)快排不如插入排序好,通常解决办法是对于小的数组不递归地使用快速排序,而采用插入排序。
实际的快排例程:

在这里插入代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值