快速排序

快速排序是一种分治的排序算法,它将一个数组分解成两个子数组,将两部分独立的排序。快速排序与归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的数组归并,已将整个数组排序;而快速排序将数组排序的方式是当两个数组都有序时整个数组自然就有序了。第一种情况递归调用发生在整个数组之前,第二种情况递归发生在处理数组之后。在归并排序中一个数组被等分成两半,而在快排中这取决于切分的位置

1.jpg
    public void sort(Comparable[] a) {
        //StdRandom.shuffle(a);
        shuffle(a);    //将数组随机打乱,这一步很重要,会影响性能
        sort(a, 0, a.length - 1);
    }

    public 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 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;
    }

    private boolean less(Comparable v, Comparable w) {
        return v.compareTo(w) < 0;
    }

    private void exch(Object[] a, int i, int j) {
        Object swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }
    
    private void shuffle(Object[] a) {
        if (a == null) throw new IllegalArgumentException("argument array is null");
        int n = a.length;
        for (int i = 0; i < n; i++) {
            int randomNum = i + uniform(n-i);
            Object temp = a[randomNum];
            a[randomNum] = a[i];
            a[i] = temp;
        }
    }
    
    //[0,n)
    private int uniform(int n) {
        if (n <= 0) throw new IllegalArgumentException("argument must be positive");
        return new Random(System.currentTimeMillis()).nextInt(n);
    }

来看看快速排序的切分轨迹,大小16的数组:

2.jpg

快速排序最理想的情况就是切分正好将数组等分,复杂度为nlgn。最坏情况是一边数组为空,复杂度为N^2/2,我们将数组在开始时打乱就是为了预防这种情况

算法改进

  1. 对于小数组,快速排序比插入排序慢,所以确定一个阀值M,推荐在5~15之间。将if (hi <= lo) return;改为if(hi <= lo + M) {InsertionSort(a, lo, hi); return;}
  2. 对于有大量重复元素的数组,将数组三分,指针lt,gt,i:规定[lo, lt-1]小于切分元素v,[lt, i-1]等于v,[i, gt]为未确定元素,[gt+1, hi]大于v;该方法对于包含大量重复元素的数组,能将排序时间从线性对数级降低到线性级别。
    public void sort(Comparable[] a) {
        shuffle(a);
        sort(a,0,a.length-1);
        assert isSorted(a);
    }
    
    private void sort(Comparable[] a, int lo, int hi) {
        if (hi <= lo) return;
        int lt = lo, gt = hi;
        Comparable v = a[lo];
        int i = lo;
        while (i <= gt) {
            int cmp = a[i].compareTo(v);
            if (cmp < 0)        exch(a, lt++, i++);
            else if (cmp > 0)   exch(a, i, gt--);
            else                i++;
        }
        sort(a, lo, lt-1);
        sort(a, gt+1, hi);
        assert isSorted(a, lo, hi);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 算法简介 是一种分治的排序算法,特点就是快,而且效率高。 基本思路 通过一趟排序将待排元素分隔成独立的两部分,其中...
    TinyDolphin阅读 3,533评论 0 3
  • 数据结构与算法——快速排序 快速排序,顾名思义,它速度很快,针对一般应用中各种不同的输入都要比其他排序算法快很多,...
    sunhaiyu阅读 3,387评论 0 3
  • 快速排序 优点:原地排序(只需要很小的辅助栈)时间复杂度:NLgN 缺点:非常脆弱。有无数例子证明许多错误能致使它...
    melouverrr阅读 615评论 0 0
  • 1.基本特点 ①原地排序(之U型要很小的辅助栈)②将长度为N的数组排序所需要的时间和NlgN成正比(平均排序)快速...
    不会code的程序猿阅读 591评论 0 0
  • 《灿烂千阳》是一本我很喜欢的书,它讲诉的是两名出生在不同时代的阿富汗女性带着各自的悲惨回忆生活着,又因为各种原因她...
    朝颜sweet阅读 353评论 1 0