zoukankan      html  css  js  c++  java
  • 快速排序(QuickSort)

    1、算法思想
         快速排序是一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。

    (1) 分治法的基本思想
         分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。

    (2)快速排序的基本思想
         设当前待排序的无序区为R[low..high],利用分治法可将快速排序的基本思想描述为:
    ①分解: 
        
     在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。
      注意:
         划分的关键是要求出基准记录所在的位置pivotpos。划分的结果可以简单地表示为(注意pivot=R[pivotpos]):
         R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys
                      其中low≤pivotpos≤high。
    ②求解: 
         
    通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。
    ③组合: 
        
     因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。

    算法结构:

    1 void QuickSort(ElemType R[],int low,int high)
    2 {
    3     if(low<high)//边界条件,递归跳出的条件
    4     {
    5         int pivotpos = Partition(R,low,high);//划分
    6         QuickSort(R,low,pivot-1);//对左区间递归排序
    7         QuickSort(R,pivot+1,high);//对右区间递归排序
    8     }
    9 }

    快速排序算法的关键在于划分操作,同时快速排序算法的性能也主要取决于划分操作的好坏。

    以下是划分算法的一种方法:

     1 int Partition(ElemType R[],int low,int high)
     2 {
     3     ElemType pivot = R[low];//保存枢轴
     4     while(low<high)
     5     {
     6         while(low<high&&R[high]>=pivot)//从右边寻找小于枢轴的值
     7             --high;
     8         R[low]=R[high];//覆盖
     9         while(low<high&&R[low]<=pivot)//从左边寻找大于枢轴的值
    10             ++low;
    11         R[high]=R[low];//覆盖
    12     }
    13     A[low]=pivot;//基准记录已被最后定位
    14     return low;//return high也是可以
    15 }

    2、性能分析
         快速排序的时间主要耗费在划分操作上,对长度为k的区间进行划分,共需k-1次关键字的比较。

    (1)最坏时间复杂度
         最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。
         因此,快速排序必须做n-1次划分,第i次划分开始时区间长度为n-i+1,所需的比较次数为n-i(1≤i≤n-1),故总的比较次数达到最大值:
                   Cmax = n(n-1)/2=O(n2)
         如果按上面给出的划分算法,每次取当前无序区的第1个记录为基准,那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中关键字最小(或最大)的记录,则快速排序所需的比较次数反而最多。

    (2) 最好时间复杂度
         在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:
            0(nlgn)
    注意:
         用递归树来分析最好情况下的比较次数更简单。因为每次划分后左、右子区间长度大致相等,故递归树的高度为O(lgn),而递归树每一层上各结点所对应的划分过程中所需要的关键字比较次数总和不超过n,故整个排序过程所需要的关键字比较总次数C(n)=O(nlgn)。
         因为快速排序的记录移动次数不大于比较的次数,所以快速排序的最坏时间复杂度应为0(n2),最好时间复杂度为O(nlgn)。

    (3)基准关键字的选取
         在当前无序区中选取划分的基准关键字是决定算法性能的关键。
      ①"三者取中"的规则
         "三者取中"规则,即在当前区间里,将该区间首、尾和中间位置上的关键字比较,取三者之中值所对应的记录作为基准,在划分开始前将该基准记录和该区伺的第1个记录进行交换,此后的划分过程与上面所给的Partition算法完全相同。

      
    ②取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准
         选取基准最好的方法是用一个随机函数产生一个取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准,这相当于强迫R[low..high]中的记录是随机分布的。用此方法所得到的快速排序一般称为随机的快速排序。具体算法【参见教材】
    注意:
         随机化的快速排序与一般的快速排序算法差别很小。但随机化后,算法的性能大大地提高了,尤其是对初始有序的文件,一般不可能导致最坏情况的发生。算法的随机化不仅仅适用于快速排序,也适用于其它需要数据随机分布的算法。

    (4)平均时间复杂度
         尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。

    (5)空间复杂度
         快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为O(lgn),故递归后需栈空间为O(lgn)。最坏情况下,递归树的高度为O(n),所需的栈空间为O(n)。

    (6)稳定性
         快速排序是非稳定的,例如[2,2,1]。

  • 相关阅读:
    只能输入正整数 以及常用的正则表达式 (转载)
    SVN cleanup操作反复失败解决办法 (转载)
    关于${pageContext.request.contextPath}的理解 (转载)
    Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds
    一则Oracle EXP导出报错的解决办法(转载)
    js数组的sort排序详解(转载)
    JavaScript arguments对象(转载)
    转 :meta name的含义
    一些小问题
    005.JMS可靠性机制
  • 原文地址:https://www.cnblogs.com/houjun/p/4868652.html
Copyright © 2011-2022 走看看