快速排序

        毫无疑问,快速排序是最流行的排序算法,因为有充足的理由,在多数情况下,快速排序都是最快的,执行时间为o(N*logN)级。(这只是对内部排序或者说随即存储器内的排序而言,对于在磁盘文件中的数据进行的排序,其它的排序算法可能更好。)快速排序是在1962年由C.A.R.Hoare发现的。
       为了理解快速排序算法,对于 划分算法应该非常熟悉。快速排序算法本质上通过把一个数组划分为两个子数组,然后递归地调用自身为每一个子数组进行快速排序来实现的。但是,对这个基本的设计还需要进行一些加工。算法还必须要选择枢纽以及对小的划分区域进行排序。看过这个主要算法的简化代码后,还要对其进行求精。

快速排序算法
      
基本的递归的快速排序算法的代码相当简单。下面就是一个实例:
       public void recQuickSort(int left,int right)
       {
             if(right-left<=0)                                          //    if size is 1,
             {
                   return;                                                   //it's already  sorted
             }
             else
             {
                                                                                  // partition range
                   int partition=partitionIt(left,right);
                    recQuickSort(left,partition-1);            //soft left side
                    recQuickSort(partition+1,right)        //sort right side                  
             }
       }
       正如大家看到的一样,有三个基本的步骤:
       1.  把数组或者子数组划分成左边(较小的关键字)的一组合右边(较大的关键字)的一组。
       2.  调用自身对左边的一组进行排序。
       3.  再调用自身对右边的一组进行排序。
       经过一次划分之后,所有在左边子数组的数据项都小于在右边子数组的数据项。只要对左边子数组和右边子数组分别进行排序,整个数组就是有序的了,如何对子数组进行排序呢?通过递归的调用排序算法自身就可以。
       传递给recQuickSort()方法的参数决定了要排序数组(或者子数组)的左右两端的位置。这个方法首先检查数组是否只包含一个数据项。如果数组只包含一个数据项,那么就定义数组已经有序,方法立即返回。这是这个递归过程的基值(终止)条件。
         如果数组包含两个或者更多的数据项,算法就调用partitionIt()方法对这个数组进行划分。方法返回分割边界的下标数值(partition),它指向右边(较大的关键字)子数组最左端的数据项。划分标记给出两个子数组的分界。
       对数组进行划分之后,recQuickSort()递归地调用自身,数组左边的部分调用一次,对从left到partition-1位置上的数据项进行排序,数组右边的部分也调用一次,对从partition+1到right位置上的数据项进行排序。注意这两个递归调用都不包含数组下标为partition的数据项。为什么不包含这个数据项?难道下标为partition的数据项不需要排序吗?这个原因在于枢纽的选择方法。

选择枢纽
       partitioit()方法应该使用什么样的枢纽呢?以下是一些相关的思想:
       >应该选择具体的一个数据项的关键字的值作为枢纽:这个枢纽项称为 piovt(枢纽)。
       >可以选择任意一个数据项作为枢纽。为了简便,我们假设总是选择待划分的子数组最右端的数据项作为枢纽。
       >划分完成之后,如果枢纽被插入到左右子数组之间的分界处,那么枢纽就落在排序之后的最终位置上了。
       最后一条听起来似乎是不可能的,但请记住,正是因为使用枢纽的关键字的值来划分数组,所以划分之后的左边子数组包含的所有数据项都小于枢纽,右边子数组的所有数据项都大于枢纽。枢纽开始时在右端,但是如果以某种方式把它放在两个子数组之间,枢纽就会在正确的位置上了,也就是说,在它最终排序的位置上了。
       因为不能真正像图中显示的那样那一个数组分开,所以这个图只是一个想象的情况。那么怎样才能把枢纽移动到它的正确的位置上来呢?
       可以把右边子数组的所有数据项都向右移动一位,以腾出枢纽的位置。但是,这样作基地小既低效又不必要。记住尽管右边子数组的所有数据项都大于枢纽,但它们都还没有排序,所以它们可以在右边子数组内部移动,而没有任何影响。因此,为了简化把枢纽插入正确位置的操作,只要交换枢纽和右边子数组的最左端的数据项就可以了。这个交换操作还把枢纽放在了它正确地位置上,也就是左右子数组之间。
       当枢纽被换到分界的位置时,它落在它最后应该在的位置上。以后所有的操作或者发生在左边或者发生在右边,枢纽本身不会再移动了。
       为了把选择枢纽的过程合并到recQuickSort()方法中,用一个明显的语句为枢纽赋值,并且把枢纽的值作为参数传递给partitionIt()。代码如下:
       public void recQuickSort(int left,int right)
       {
             if(right-left<=0)                                          //    if size is 1,
             {
                   return;                                                   //it's already  sorted
             }
             else                                                            //size is 2 or larger
             {
                   long pivot=theArray[right];                 // rightmost item
                                                                                 //partition range
                   int partition=partitionIt(left,right,pivot);
                    recQuickSort(left,partition-1);            //soft left side
                    recQuickSort(partition+1,right)        //sort right side                  
             }
       }      // end recQuickSort()
      
       当使用选择数组最右端的数据项作为枢纽方案时,需要修改partitionIt()方法,从划分的过程中把最右端的数据项排除在外。毕竟,这个数据项在划分过程完成之后应该在什么位置已经很清楚:在两个子数组之间的划分点上。划分过程完成之后,还需要把枢纽从数组的最右端移动到划分点(patition)的位置上。

未完,待续。。。。。。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值