快速排序算法。。

摘 要: 快速排序是排序算法中性能较好的一种,但存在对数据基本有序的情形下的性能瓶颈问题。为了保证快速排序在任何情况下的高效性,在对快速排序算法的时间效率进行充分的分析的基础上,指出支点元素的选取是影响快速排序算法效率的主要因素。提出了一种随机选择支点元素的快速快排方法,很好地避免了最坏情况的发生。通过实验验证了改进算法的正确性和高效性。
中国论文网 http://www.xzbu.com/8/view-4477640.htm
  关键词: 快速排序算法; 支点元素; 时间效率; 随机化快速排序
  中图分类号: TN911?34; TP301.6 文献标识码: A 文章编号: 1004?373X(2013)20?0054?03
  快速排序(Quicksort)是一种基于比较、划分的排序方法。它基本思想是:在待排元素集S中选择一个元素x作为支点(Pivot),通过一趟排序将要排序的数据分割成独立的两个子序列SL和SR,其中左部分SL的所有元素都小于或等于支点元素,而右部分SR的所有元素都大于或等于支点元素(升序排序) ,其状态如图1所示。然后按此方法对这两部分数据分别再进行快速排序,以此达到整个待排元素有序,整个排序过程可以递归进行,其递归的深度可用二叉树表示,如图2所示。从算法的思想可以得出,快速排序算法是利用分治技术的典型例子。通常,大家公认快速排序是基于比较的排序方法中平均比较次数最少、速度最快的排序算法,平均时间复杂度为O(nlbn)。但是,若支点元素选择不当,在划分的两个子序列元素个数极度不平衡时,快速排序有效率会急剧下降,最坏情况下快速排序将蜕变为冒泡排序,其时间复杂度为O(n2),算法的递归深度就变成一棵深度为n的单支二叉树。因此,保证快速排序在任何情况下高效性,近年来被国内学者从各种不同的角度进行了改进与优化[1?6]。
  本文在对传统快速排序算法的优点及缺点进行充分分析的基础上,指出影响快速排序的关键因素,提出一种随机化的高效排序方法。它对待排序的数据初态没有任何要求,或者说可以让任何的数据初态在排序时达到均匀分布,从而使任何输入数据达到O(nlog n)的时间复杂度。
  1 传统的快速排序算法与分析
  1.1 传统的快速排序算法
  快速排序算法采用了分治技术,其分治步骤为:首先,问题划分。将求解问题分成若干大小不等的子问题;其次,递归求解。独立地解决这些子问题;最后,合并解。将子问题的解归并成原问题的解。由于快速排序可以采用就地重排,合并解不需要花费时间。因此影响算法的最关键的问题是支点元素的选择方法是否适当,是否可以将问题均等的划分。
  传统的快速排序算法是从待排数据两端选取一个元素作为支点元素,其递归快速排序算法QuickSort如下:
  QuickSort(&S,L,H){
  //对待排元素S[L..H]进行快速排序
  int m; //标识上次划分支点元的位置
  if l  {
  m ←PARTITION(S,L,H)
  //调用分治法找到m的值
  QuickSort(S,L,m-1)
  //对左区间递归快排
  QuickSort(S,m+1,H)
  //对右区间递归快排
  }}
  对待排元素集S进行排序,最初的调用是QuickSort(S,1,length[S])。而快速排序算法的关键是PARTITION过程,它对子元素集S [L..H]进行一趟快速排序或一次划分:
  一次划分PARTITION的算法步骤为:
  Step1 首先定义两个变量i和j,并初始化i=L,j=H ;
  Step2 选取第一个待排元素S[L]作为支点元素,并将支点元赋值给pivot,即执行pivot =S[L];
  Step3 从j位置开始由后向前比较(j-- ),找到第一个小于key(支点元素的关键字)的元素S[j],交换S[i]与S[j];同时,i++。
  Step4 从i位置开始由前向后比较 (i++),找到第一个大于key(支点元素的关键字)的S[i],S[i]与S[j]交换;同时,j--。
  Step5 重复执行Step3,Step4,直到i=j。并将S[i]←pivot,返回i值。
  1.2 传统快速排序算法的效率分析
  算法的时间开销主要花费在划分PARTITION操作上,对长度为n的数据元素进行一次划分,共需n-1次元素的比较。
  (1)最好情况时间复杂度
  最好情况下,快速排序的每次划分所选取的支点元素恰好都是当前待排数据的“中值”元素,每次划分的结果为:支点元素的左、右两个子表的长度大致相等,即对半划分。在此情况下,可得知快速排序要做lg n趟划分,时间复杂度为C(n)=O(nlg n)。
  (2)最坏情况时间复杂度
  最坏情况下,快速排序的每次划分选取的支点元素恰好都是当前待排数据中关键字最小(或最大)的元素,每次划分的结果为:支点左边的子表为空(或右边的子表为空),而另一个非空的子表中元素个数仅比划分前的待排数据中元素个数少1个。 在此情况下,快速排序就要做n-1次划分,而第i次划分开始时区间长度为n-i+1,所需的比较次数为n-i(1≤i≤n-1),故总的比较次数达到最大值:
  如果按上面给出的划分算法,当待排元素集S已按递增有序(或递减有序)排列时,每次取当前待排数据的第1个元素为支点,那么每次划分所取的支点就是当前待排数据中关键字最小(或最大)的记录,则快速排序所需的比较次数达到最多。即当元素基本有序时,快速排序的效率反而很低,不及冒泡排序及插入排序。   (3)平均时间复杂度
  尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快的,快速排序亦因此而得名。它的平均时间复杂度为O(nlg n)[4]。
  在此选择同一量级的堆排序进行了比较测试。在相同的环境下,基于C++语言平台,分以不同规模(100,1 000,10 000,100 000)的随机数作为测试数据集。在程序中根据数据个数的不同产生的随机整型数组,然后分别让不同的排序算法来进行从小到大的排序。这里两种排序算法在相同的输入规模中原始无序数据都是一样的,以此来保证实验的公正性。每个排序算法中加入计数器来记录排序过程中的比较次数,同时利用计时函数得出排序时间。
  表1为输入数据规模分别为100,1 000,10 000,100 000时两个算法的排序时间对比。实验结果表明,一般情况下,快速排序的效率的确比堆排序要高。
  表1 堆排序与快速排序时间比较 ms
  (4)空间复杂度
  快速排序算法是一个递归算法,因此,系统会自动开辟一个栈来辅助算法的执行。最坏情况下,递归树的高度为O(n),所需附加栈的空间为线性量级O(n)。一般情况下,如图2所示其递归树的高度为O(lg n), 执行算法所需附加栈的空间为对数量级O(lg n)。
  2 快速排序算法的改进
  影响快速排序算法效率的主要因素是支点元素的选取。若所选择的支点元素应能够将数组S分成大小几乎相等的部分,就能保证快速排序算法的高效性。
  假设S由n个不同元素组成,最好的选择方法是选择S中元素的中值作为支点元素。比如,初始元素的关键字为:333,4,11,23,57,要应选择23中值作为支点元素。尽管有些理论上好的算法可以找到待排元素的中值,但由于开销过大使得快速排序无法在实际中得到实用[7]。比如,先对待排序的数据进行统计、求平均来选取出最佳的支点元素,以确保快速排序的每一次划分位置都正好处于待排序序列的正中间。其算法的本质是选取了最合适的支点,但选取合适的支点本身是一个很浪费时间的操作,因此其方法只能在某些特定情况下提高排序效率,而在另一些情况下反而效率低于基本快速排序。
  针对快速排序支点元素的选取,能够有效改进排序的效率,而又不额外增加开销的主要有以下两种方法。
  第1种方法是“三者取中”的规则[8?9]。普遍使用的方法:首先,比较待排数据中第一 、中间和最后一个位置上元素的关键字,取三者之中值为支点元素。其次,在划分开始前将该支点元素和该区间的第1个元素进行交换,此后的划分过程与1.1所给的PARTITION算法完全相同。实践表明,采用三元素取中值的规则只需要几条if语句的判断即可,在时间上、空间上不会增加额外的开销,但结果可以大大改善快速排序在最坏情况下的性能。第2种方法就是本文所提出的随机化快速排序。算法思路:每趟划分选取哪一位置上的元素作为支点不是固定的,而是用随机数产生器Random(l,h) 随机选取位于l和h之间的随机数t(l≤t≤h),用S[t]作为支点元素。这就打破了传统快速排序对数据元素初始输入的依赖,相当于S[l..h]中的元素是随机分布的。
  可以看出,随机化的快速排序与一般的快速排序算法差别很小。选择l和h之间中任意一个元素作为支点。只需要将选中的支点元素与第一个位置上的元素互换swap(S[t],S[l]),之后同样调用 PARTITION(S,l,h)算法进行划分。随机化之后,算法的时间空间开销没有增加,但性能却得到了改善,尤其是对待排序元素基本有序的情况下,不可能导致最坏情况的发生。可以严格地证明划分的每一步以非常大的概率导致均匀划分,与初始输入分布无关[10]。经实验对比,在元素有序情况下,两种排序算法在规模分别为100,200,500时比较次数的统计情况对比,如表2所示。
  表2 元素有序两种排序比较次数统计
  同时,算法的随机化不仅仅适用于快速排序,也适用于其他需要数据随机分布的算法。
  3 结 语
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值