排序算法

来源:互联网 发布:淘宝女装分类宝贝图片 编辑:程序博客网 时间:2024/05/29 02:22
比较 交换 时间 空间 是否稳定 备注 选择排序 N2/2 N N2 1 否 对空间要求低,对初始顺序不敏感,移动成本低 插入排序 N2/4(平均) N2/4 N~N2 1 是 对部分有序的效率高 希尔排序 - - N2 1 否 插排升级版 归并排序(顶->下) NlgN - NlgN N 是 对空间有要求,速度较快,且有改善空间,非原地排序 归并排序(底->上) NlgN - NlgN N 是 适用于链表结构,非原地排序 快速排序 多于归并 远小于归并 NlgN 1 否 结构精巧,注重边界条件,时空消耗小 优先队列/堆排序 - - NlgN 1 否 局部优先性

归并排序(顶->下)

    private static void sort(Comparable[] a, int lo, int hi){        if(lo >= hi)    return;        int mid = lo + (hi-lo)/2;        sort(a, lo, mid);        sort(a, mid+1, hi);//mid+1保证了不会进入迭代死循环        merge(a, lo, mid, hi);    }

归并排序(顶->下)的优化:

  1. 对小数组采用插排
  2. 判断sort()后的数组是否有序,即a[mid] < a [mid+1],若成立则跳过merge()
  3. 不将元素复制进辅助数组

对于第三点优化,练习2.2.11的部分代码段如下:

    private static void merge(Comparable[] src, Comparable[] dst, int lo, int mid, int hi) {//得到后者dst有序        // precondition: src[lo .. mid] and src[mid+1 .. hi] are sorted subarrays        assert isSorted(src, lo, mid);        assert isSorted(src, mid+1, hi);        int i = lo, j = mid+1;        for (int k = lo; k <= hi; k++) {            if      (i > mid)              dst[k] = src[j++];            else if (j > hi)               dst[k] = src[i++];            else if (less(src[j], src[i])) dst[k] = src[j++];   // to ensure stability            else                           dst[k] = src[i++];        }        // postcondition: dst[lo .. hi] is sorted subarray        assert isSorted(dst, lo, hi);    }    private static void sort(Comparable[] src, Comparable[] dst, int lo, int hi) {        // if (hi <= lo) return;        if (hi <= lo + CUTOFF) {             insertionSort(dst, lo, hi);            return;        }        int mid = lo + (hi - lo) / 2;        sort(dst, src, lo, mid);//在这里可以看出与sort输入的相反了        sort(dst, src, mid+1, hi);        // if (!less(src[mid+1], src[mid])) {        //    for (int i = lo; i <= hi; i++) dst[i] = src[i];        //    return;        // }        // using System.arraycopy() is a bit faster than the above loop        if (!less(src[mid+1], src[mid])) {            System.arraycopy(src, lo, dst, lo, hi - lo + 1);            return;        }        merge(src, dst, lo, mid, hi);//依据该函数的具体构成可以看出结果是保证了dst有序。所以得出当前sort的输出是保证dst有序的    }    /**     * Rearranges the array in ascending order, using the natural order.     * @param a the array to be sorted     */    public static void sort(Comparable[] a) {        Comparable[] aux = a.clone();        sort(aux, a, 0, a.length-1);  //所以将原数组a放在后面        assert isSorted(a);    }

归并排序(底->上)

取消了sort(a, lo, hi)并修改了sort(Comparable[] a)

    public static void sort(Comparable[] a){        int N = a.length;        aux = new Comparable[N];        for(int size = 1; size < N; size = 2*size){            for(int lo = 0; lo < N-size; lo += 2*size){//lo<N-sz因为merge仅仅需要保证一个数组的长度,另一个长度不作要求                merge(a, lo, lo+size-1, Math.min(lo+2*size-1, N-1));            }        }    }

注:自底向上的归并适合用于链表结构的数据集。


快速排序

继承于归并,重点在于切分,即通过切分保证切分点左边的数组总小于切分点右边的数组。

    public static void sort(Comparable[] a){        StdRandom.shuffle(a);                                   //记得随机化        sort(a, 0, a.length-1);    }    private static void sort(Comparable[] a, int lo, int hi){        if(lo >= hi)    return;        int j;        j = partition(a, lo, hi);        sort(a, lo, j-1);                                       //记得-1,防止无限迭代        sort(a, j+1, hi);                                       //记得+1,防止无限迭代    }    private static int partition(Comparable[] a, int lo, int hi){        Comparable v = a[lo];        int i = lo;        int j = hi+1;                                           //记得+1        while(true){            while(less(a[++i], v))      if(i == hi) break;      //先++在i            while(less(v, a[--j]))      if(j == lo) break;            if(i >= j)  break;                                  //防止过度交换            exch(a, i, j);        }        exch(a, lo, j);        return j;    }

快速排序优化

  1. 小数组采用插排
  2. 改善切分点
  3. 考虑重复元素数组的快排改进——三向切分(基于信息量排序)

补充
采用comparator接口代替comparable来使得排序规则具有选择性。

优先队列/堆排序

JAVA实现的类:

Class PriorityQueue<E>
一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException)。

基于堆排序的优先队列可以在以较小的代价下,实现局部有序。
来看一下堆排序方法,其核心原理是利用树结构,父节点总是大于子节点,每次插入新元素,只需要比较父节点和其中一个子节点就能保证根节点是最大的,并且实现在取出根节点后不需要在重新比较所有元素,而仅仅用部分比较就可以继续保持局部有序。因此堆排序可以将比较次数减少为树高。由此也可以看出,对于二叉堆,多叉堆的效率会更高,但是,效率的提升是有限的,其最终改变的就是从log2变为log3,都是线性对数级别。

原创粉丝点击