排序之快速排序

来源:互联网 发布:js select改变事件 编辑:程序博客网 时间:2024/05/16 19:21

                                        排序之快速排序

     接着归并排序继续整理,我们知道了归并排序的关键在于归并,分解的部分很微小,因而被称为归并排序,这是一种“先享受后付出代价”的思维方式,同时归并排序还存在一个缺点,那就是它是一个异地排序,即在排序的过程中需要一个额外的存储空间。与之相对的,在1962年由C.A.R Hoare提出了另一种思路的排序算法——快速排序。该算法也被誉为计算史上的伟大算法,应用十分广泛。

     快速排序算法的思路是:在把一个序列分开时,不是从中间位置分解,而是按照元素的大小分开为两个一大一小的子序列,即一个子序列的所有元素均大于另一个子序列的所有元素。这样的话,因为这两个子序列之间的相对次序已经正确,因而在合并的时候就不需要任何的时间,这是一种“先付出再享受”的思维方式。显然,快速排序也使用了分治算法,同时快速排序属于原地排序,即不需要额外的存储空间。

     快速排序的过程大体分为四步:1.选取分界点 2.分解:把小于分界点的数字全部都移动到分界点的左边,把大于分界点的数字全都移动到分界点右边 3.治之:递归的对两个子序列进行快速排序 4.合并:将排好序的子序列合并为一个大的序列。

    快速排序的关键显然在于分解,在这里给出一种可行的做法,先给出代码,再做具体解释:

 //获得调整后分界点位置的函数,初始时把分界点置为数组的第一个元素

int AdjustArray(int *s,int l, int r){       inti,j;       i=l;       j=r;       intx = s[l];       while(i< j)       {              while(i< j && s[j] > x)                     j--;              if(i< j)              {                     s[i]= s[j];                     i++;              }               while(i< j && s[i] < x)                     i++;              if(i< j)              {                     s[j]= s[i];                     j--;              }       }       s[i]= x;       returni;}

    上述代码中,我们假定序列的第一个数字为分界点,首先,我们要先把它的值取出来保存,(即x=s[l]),以便于我们最后把它放到一个合适的位置上。我们还要取两个指针,分别指向序列的头部和尾部,接下来我们先从序列的尾部逐个向前寻找一个比x小的数字,直到找到为止,此时,我们把该数取出,把它的值覆盖到序列的第一个数字;接下来该指针的位置保持不动,把指向头部的指针的位置后移一个单位,并不断向后逐个搜寻一个比x大的数,直到找到为止,此时把该数取出,覆盖到对应的从尾部搜寻到的第一个数字的位置上。至此,第一次交换完成,然后在i<j的前提下,进行循环的操作即可,直到i=j。看明白没?简单的说,就是先取定第一数字为分界点,保存起来,然后从后往前搜寻比x小的数,再从前往后搜寻比x大的数,如此交替进行,同时把找到的值依次覆盖到上一次找到的值的位置上,最后把保存的x值覆盖到i=j的位置上即可。下面举例来看一下:

 

初始序列:                         3   2  4    5   1    6  (先把3赋给x)

第一次搜寻后结果:         1  2   4    5   1    6

第二次搜寻后结果:         1  2   4    5  4    6

第三次搜寻后结果:         1  2   3    5  4    6(此时把3放到合适的位置,以3为分界点的序列,左边均比三小,右边均比三大,此时一次分解完成)

 

 接下来我们要返回该分界点的位置,然后再分别对分界点的左边与右边序列进行递归,直到每个序列只有一个数字时,递归结束,此时该序列即满足有序的要求了。代码如下:

void sort_quick(int *s, int l,int r){  if(l < r)  {         int i =AdjustArray(s,l,r);         sort_quick(s,l,i-1);         sort_quick(s,i+1,r);  } }


   最后,再来blabla有关时间复杂度的问题,不同于归并排序的时间复杂度是稳定的,即在最好、最差或平均情况下归并排序的时间复杂度均为O(nlogn)。快速排序在最坏情况下时间复杂度为O(n^2)在最好和平均情况下时间复杂度均为O(nlogn),而且可以证明,只要在分解时不产生空的子序列,则不管产生的两个子序列长度差异有多大,哪怕是1%对99%,其时间复杂度仍未O(nlogn),也就是说快速排序具有很强的韧性,只要给他一丁点的机会,它都会取得最好的结果,这也正是它的优势所在。(注:时间复杂度的证明有兴趣可以具体证明)

0 0
原创粉丝点击