快速排序算法

一、快速排序算法的基本特性
时间复杂度:O(n*lgn)
最坏:O(n^2)
空间复杂度:O(n*lgn)
不稳定。

快速排序是一种排序算法,对包含n个数的输入数组,平均时间为O(nlgn),最坏情况(已经排好序)是O(n^2),最好情况(完全无序)是O(nlgn)。
通常是用于排序的最佳选择。因为,基于比较的排序,最快也只能达到O(nlgn)。


二、快速排序算法的描述
算法导论,第7章
快速排序时基于分治模式处理的,
对一个典型子数组A[p...r]排序的分治过程为三个步骤:
1.分解:
A[p..r]被划分为俩个(可能空)的子数组A[p ..q-1]和A[q+1 ..r],使得
A[p ..q-1] <= A[q] <= A[q+1 ..r]
2.解决:通过递归调用快速排序,对子数组A[p ..q-1]和A[q+1 ..r]排序。
3.合并。

 

三、快速排序算法

快速排序算法的版本很多,这其实也是基于每个人自己的思维方式不同,接受和理解方式不同来区分。并没有明显的优劣之分,只要自己能理解,就可以啦!接下来我要说的就是算法导论上的那个版本。

QUICKSORT(A, p, r)
1 if p < r
2    then q ← PARTITION(A, p, r)   //关键
3         QUICKSORT(A, p, q - 1)
4         QUICKSORT(A, q + 1, r)


快速排序算法的关键是PARTITION过程(数组划分),它对A[p..r]进行就地重排:
PARTITION(A, p, r)
1  x ← A[r]    //以最后一个元素,A[r]为主元
2  i ← p - 1
3  for j ← p to r - 1    //j从p指向的是r-1,不是r。
4       do if A[j] ≤ x
5             then i ← i + 1
6                  exchange A[i] <-> A[j]
7  exchange A[i + 1] <-> A[r]
8  return i + 1

举个例子说明一下这个数组划分的过程:2   8   7   1   3   5   6   4

注意:以最后一个元素4为主元,且i,j俩指针都从头出发,j 一前,i 一后。i 指元素的前一个位置,j 指着待排序数列中的第一个元素。

i p/j                              r

  2   8   7   1   3   5   6   4(主元)
j指的元素是2,因为2<=4(主元),于是i++,i也指到元素2,所以A[i] <-> A[j],也就是2和2互换,原数组不变。
然后,j继续后移,直到指向1。

  p/i           j                  r

  2   8   7   1   3   5   6   4(主元) 

j指向元素1,因为1<=4(主元),于是i++,i指向了8,所以8与1交换。

数组变成了:
  p   i         j                  r
  2   1   7   8   3   5   6   4

  p   i              j             r
  2   1   7   8   3   5   6   4

然后j继续后移,指向了元素3,因为3<=4(主元),于是i++,i这是指向了7,于是7与3交换。
数组变成了:
  p        i         j             r
  2   1   3   8   7   5   6   4

j继续后移,一直到r-1的位置,都发现没有再比4小的数,所以,执行到了PARTITION过程的最后一步,

即上述PARTITION(A, p, r)代码部分的 第7行。
因此,i后移一个单位,指向了8
  p             i              j    r
  2   1   3   8   7   5   6   4
执行A[i + 1] <-> A[r],即8与4交换,所以,数组最终变成了如下形式,
  2   1   3   4   7   5   6   8
至此,快速排序第一趟完成。


4(主元)把整个数组分成了俩部分,2 1 3和7 5 6 8,再递归对这俩部分分别快速排序。
i p/j
  2   1   3(主元)
2与2互换,不变,然后又是1与1互换,还是不变,最后,3与3互换,不变,
最终,3(主元)把2 1 3,分成了俩部分,2 1和3。
再对2 1,递归排序,最终结果成为了1 2 3。

7 5 6 8(主元),7、5、6、都比8小,所以第一趟,还是7 5 6 8,
不过,此刻8把7 5 6 8,分成了  7 5 6和8。[7 5 6->5 7 6->5 6 7]
再对7 5 6,递归排序,最终结果变成5 6 7 8。

ok,所有快速排序的过程,全部分析完成。

最后还得再说说快速排序中最关键的PARTITION过程(数组划分),由上述过程,可看出每一次PARTITION过程中,j扫描了整个数组一遍,只要一旦遇到比4(主元)小的元素,i 就++,然后,kj、ki交换。那么,为什么当j找到比4小的元素后,i 要++了? 你想了,如果i始终停在原地不动,与kj 每次交换的ki不就是同一个元素了么?如此,还谈什么排序?。所以,j在前面开路,i跟在j后,j只要遇到比4小的元素,i 就向前前进一步,然后把j找到的比4小的元素,赋给i,然后,j才再前进。

     打个比喻就是,你可以这么认为,i所经过的每一步,都必须是比4小的元素,否则,i就不能继续前行。好比j 是先行者,为i 开路搭桥,把小的元素作为跳板放到i 跟前,为其铺路前行啊。

    于此,j扫描到最后,也已经完全排查出了比4小的元素,只有最后一个主元4,则交给i处理,因为最后一步,exchange A[i + 1] <-> A[r]。这样,不但完全确保了只要是比4小的元素,都被交换到了数组的前面,且j之前未处理的比较大的元素则被交换到了后面,而且还是O(N)的时间复杂度,你不得不佩服此算法设计的巧妙。

 

 

四、快速排序算法的时间复杂度

    ok,大概理解了快速排序,那么,也能很快的判断出:快速排序算法的平均时间复杂度,即为O(nlgn)。为什么了?因为你看,j,i扫描一遍数组,花费用时多少?对了,扫描一遍,当然是O(n)了,那样,扫描多少遍列,lgn到n遍,最快lgn,最慢n遍。且可证得,快速排序的平均时间复杂度即为O(n*lgn)。

    PARTITION可能做的最平衡的划分中,得到的每个子问题都不能大于n/2。因为其中一个子问题的大小为|_n/2_|。另一个子问题的大小为|-n/2-|-1。

    在这种情况下,快速排序的速度要快得多。为:

      T(n)<=2T(n/2)+O(n).可以证得,T(n)=O(nlgn)。

 

 

五、Java代码实现快速排序算法

public class QuickSort {
    /**
     * 快速排序的主方法
     * @param array :待排序数组
     * @param begin :数组第一个元素的下标(其实就是0)
     * @param end :数组最后一个元素的下标(其实就是数组长度-1)
     * @return
     */
    public static int[] quickSort(int[] array,int begin,int end ) {
        
        if(begin < end){
            int k = partition(array, begin, end);
            quickSort(array,begin,k-1);
            quickSort(array,k+1,end);
        }
        return array;
    }
    public static int partition(int[] data,int head,int hi){
        
         int key=data[hi];  //以最后一个元素,data[hi]为主元
         int i=head-1;
         for(int j=head;j<hi;j++)   ///注,j从p指向的是r-1,不是r。
         {
          if(data[j]<=key){
           i=i+1;
           swap(data,i,j);
          }
         }
         swap(data,i+1,hi);   
         return i+1;
        
    }
    public static void swap(int[] data ,int i ,int j){
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }
    
    public static void main(String[] args) {
        int[] array = {2,8,7,1,3,5,6,4,9};
        int len = array.length-1;
        array = quickSort(array, 0, len);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]);
        }
    }
}

 

整理自:http://blog.csdn.net/v_JULY_v/article/details/6211155

            http://blog.csdn.net/v_JULY_v/article/details/6116297

posted on 2016-08-12 11:01  毛先森  阅读(369)  评论(0编辑  收藏  举报

导航