快速排序

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

快速排序_第1张图片
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张图片
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);
    }

你可能感兴趣的:(快速排序)