快速排序

定义:快速排序是一种分治的排序算法。选取数组中任意一个元素,将一个数组分成两个子数组,这两个子数组分别都比所选取元素小和大,然后将两部分独立地排序。

java代码:

public class Quick{
    public static void sort(Comparable[] a){
        sort(a, 0, a.length-1);
    }

    private static void sort(Comparable[] a, int lo, int hi) {
        if(hi <= lo) return;
        int j = partition(a, lo, hi);
        sort(a, lo, j-1);
        sort(a, j+1, hi);
    }

    private static int partition(Comparable[] a, int lo, int hi) {
        int i = lo, j = hi+1;
        Comparable v = a[lo];
        while(true){
            while(less(a[++i], v)) if(i == hi) break;
            while(less(v, a[--j])) if(j == lo) break;
            if(i >= j) break;
            exch(a, i, j);
        }
        exch(a, lo, j);
        return j;
    }
}

复杂度:
假设将长度为N的无重复数组排序。
比较次数:
CN 为将N个不同元素排序平均所需的比较次数。显然 C0=C1=0 ,对于 N>1 ,由递归程序可以得到以下归纳关系:
CN=N+1+C0+C1+...+CN2+CNN+CN1+CN2+...+C0N

将该等式将去N-1时的相同等式可得:
NCN(N1)CN1=2N+2CN1

整理等式并将两边除以 N(N+1):
CNN+1=CN1N+2N+1

归纳法推导可得:
CN=2(N+1)(13+14+15+...+1N+1N+1)

括号内的量是曲线 2x 下从 3 N的离散近似面积加一,积分得到 CN2NlnN 。注意到 2NlnN1.39NlgN ,也就是说平均比较次数只比最好情况多39%。

例子:粗体是作为比较的元素,斜体是要交换的两个元素
原序列:[54 29 07 33 18 70 42 03 46 01 90 89 86 56 94]
54
[54 29 07 33 18 70 42 03 46 01 90 89 86 56 94]
[54 29 07 33 18 01 42 03 46 70 90 89 86 56 94]
[46 29 07 33 18 01 42 03] 54 [70 90 89 86 56 94]
46
[03 29 07 33 18 01 42] 46 54 [70 90 89 86 56 94]
03
[03 01 07 33 18 29 42] 46 54 [70 90 89 86 56 94]
01 03 [07 33 18 29 42] 46 54 [70 90 89 86 56 94]
07
01 03 07 [33 18 29 42] 46 54 [70 90 89 86 56 94]
33
01 03 07 [29 18] 33 42 46 54 [70 90 89 86 56 94]
29
01 03 07 18 29 33 42 46 54 [70 90 89 86 56 94]
70
01 03 07 18 29 33 42 46 54 [70 56 89 86 90 94]
01 03 07 18 29 33 42 46 54 56 70 [89 86 90 94]
89
01 03 07 18 29 33 42 46 54 56 70 86 89 [90 94]
90
01 03 07 18 29 33 42 46 54 56 70 86 89 90 94

优化:

1.切换到插入排序

和大多数递归排序算法一样,改进快速排序性能的一个简单办法基于以下两点:

  • 对于小数组,快速排序比插入排序慢;
  • 因为递归,快速排序的sort()方法在小数组中也会调用自己。

因此,在排序小数组时应该切换到插入排序。

Java 代码:

public class Quick{
    public static void sort(Comparable[] a){
        sortAdvance(a, 0, a.length-1, 5);
    }

    private static void sortAdvance(Comparable[] a, int lo, int hi, int M){
    //转换参数M的最佳值和系统相关,但是5~15之间的任意值在大多数情况下都能令人满意
        if(hi <= lo + M){
            Insertion.sort(a, lo, hi);
            return;
        }
        int j = partition(a, lo, hi);
        sortAdvance(a, lo, j-1, M);
        sortAdvance(a, j+1, hi, M);
    }

    private static int partition(Comparable[] a, int lo, int hi) {
        int i = lo, j = hi+1;
        Comparable v = a[lo];
        while(true){
            while(less(a[++i], v)) if(i == hi) break;
            while(less(v, a[--j])) if(j == lo) break;
            if(i >= j) break;
            exch(a, i, j);
            show(a);
        }
        exch(a, lo, j);
        return j;
    }
}

2.三取样切分
如果数组中有重复的元素,可将其不归到递归中去,以减少交换次数。

private static void sort3way(Comparable[] a, int lo, int hi){
        if(hi < lo) return;
        int lt = lo, i = lo+1, gt = hi;
        Comparable v = a[lo];
        while(i <= gt){
            int cmp = a[i].compareTo(v);
            //小于v的交换到前面,也同时将v逐步移至中间
            //lt定位的是v,i定位的是最前的生元素
            if(cmp < 0)
                exch(a, lt++, i++);

            //交换前由于gt定位的元素并未被比较,所以交换后i不需要+1,即i定位的是最前的生元素
            else if(cmp > 0)
                exch(a, i, gt--);
            else
                i++;
        }
        sort3way(a, lo, lt-1);
        sort3way(a, gt+1, hi);
    }

这段代码的将数组分为三部分,将小于比较元素的放到左边,将大于比较元素的放到右边,等于比较元素的归到中间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值