快速排序

Posted 谭兄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序相关的知识,希望对你有一定的参考价值。

快速排序比一般的排序算法都要快,它是原地排序(只需要一个很小的辅助数组),且将长度为N的数组排序所需的时间与NlgN成正比.

 

基本算法

快速排序也是一种分治算法,它将一个数组分成两部分分别排序,它和归并排序是互补的.

归并排序是将一个数组分成两个子数组分别排序,并将有序子数组归并以将整个数组排序;而快速排序将数组排序的方式是子数组有序时整个数组也是有序的.

在快速排序中,切分的位置取决于数组的内容.

 

 

 

代码实现

    public static void sort(Comparable[] a){
        sort(a,0,a.length-1);
    }
    
    public static void sort(Comparable[] a, int lo, int hi){
        if(lo>=hi) return ;//递归出口
        int j = partition(a,lo,hi);    //切分元素
        sort(a,lo,j-1);    //左半部分排序
        sort(a,j+1,hi);    //右半部分排序
    }

 

快速排序递归地将子数组a[lo..hi]排序,先用partition()方法将a[j]放到一个合适的位置,然后再递归将其他元素排序.

 

 

关键在于切分,这个过程使数组满足三个条件:

对于某个j,a[j]已经排定.

a[lo]到a[j-1]中所有元素都不大于a[j].

a[j+1]到a[hi]中所有元素都不小于a[j].

 

切分方法的实现

先随意取a[lo]作为切分元素,即那个会被排定的元素.

然后从数组的左端扫描直到找到一个大于等于它的元素,再从数组的右边扫描直到找到一个小于等于它的元素. 这两个元素显然没有排定,所以我们交换他们的位置

如此继续,我们可以保证左指针i的左侧元素都不大于切分元素,右指针j的右侧元素都不小于切分元素. 当两个指针相遇时,我们只要把切分元素a[lo]和左子数组最右边元素a[j]进行交换即可,这时切分元素就留在了a[j]中了.

 

 

轨迹图

 

算法实现

    public static int partition(Comparable[] a, int lo, int hi){
        int i = lo, j = hi + 1;    //左右扫描指针
        Comparable v = a[lo];    //切分元素
        while(true){
            //扫描到左半部分大于v的元素停止
            while(less(a[++i], v)) if(i==hi) break;
            //扫描到右半部分小于v的元素停止
            while(less(v,a[--j])) if(j==lo) break;
            if(i>=j)
                break;
            //交换元素
            exch(a,i,j);
        }
        //交换切分元素a[lo]和左半部分最右边的元素a[i].因为a[lo]到a[j-1]中的所有元素都不大于a[j]
        exch(a,i,lo);
        return j;
    }

 

优化

 

1.在排序小数组时切换到插入排序

2.三取样切分(取样大小为3设中间的元素为切分元素),代价是需要计算中位数.

3.熵最优排序

 

 三向切分的快速排序

快速排序的一种改进,使快排在有大量重复元素的数据,同样能保持高效。

 

我们从左到右遍历数组,维护一个指针lt使得a[lo..lt-1]中的元素全部小于v,一个指针gt使得a[gt+1..hi]中的元素全部大于v,一个指针i使得a[lt,i-1]中的元素都等于v,a[i,gt]中的元素都还未确定.

 

 

 

一开始令i和lo相等,遍历过程中有3种情况

a[i] < v, 将a[i]和a[lt]交换,将lt和i都加一.

a[i] > v, 将a[i]和a[gt]交换,将gt减一.

a[i] == v ,将i加一.

这些操作保证数组元素不变且缩小gt-i的值(这样循环才能终止)

 

 

    public static void sort(Comparable[] a, int lo, int hi){
        if(hi <= lo) return ;
        int lt = lo, gt = hi,i = lo + 1;
        Comparator v = a[lo];
        while(i<=gt){
            int cmp = a[i].compareTo(v);
            if(cmp>0) exch(a,i,gt--);
            else if(cmp<0) exch(a,i++,lt++);
            else    i++;
        }
        //现在 a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]成立
        sort(a,lo,lt-1);
        sort(a,gt+1,hi);
    }

 

 

 算法轨迹

 

以上是关于快速排序的主要内容,如果未能解决你的问题,请参考以下文章

排序算法之快速排序

快速排序/快速选择算法

大佬的快速排序算法,果然不一样

深入理解快速排序和 STL 的 sort 算法

简单介绍一下快速排序的思想?

排序算法(02)— 快速排序算法