归并排序详解,与其他排序算法的比较

归并排序

将两个有序的数组归并成一个更大的有序数组

一种简单的方法是:将两个数组归并到第三个数组中(需要额外的空间)

更常见的做法是:避免了开辟额外空间,采用原地归并的方式

原地归并的抽象方法

    public static void merge(Comparable[] a, int lo, int mid, int hi){
        // 将大数组a分成两个小数组,a[lo, mid], a[mid, hi] 分别进行排序
        int j = mid+1;
        int i = lo;
        // 数组复制,复制到辅助数组中
        System.arraycopy(a, lo, aux, lo, hi);

        for(int k = lo; k <= hi; k++) {
            if (i > mid)             // 当左侧小数组为空时
                a[k] = aux[j++];
            else if (j > hi)        // 当右侧小数组为空时
                a[k] = aux[i++];
            // 比较左右两个数组首位,如果aux[j]小于aux[i]
            else if (less(aux[j], aux[i])) 
                // 将 aux[j] 放入原数组  并索引下移
                a[k] = aux[j++];            
            else  // 否则将 aux[i] 放入原数组  并索引下移
                a[k] = aux[i++];
        }
    }

自顶向下的归并排序

算法讲究先从上直接递归到最小数组,将最小数组排序

步骤:

  1. 通过递归将数组不断二分,直到小数组只含有两个元素为止
  2. 将只含有两个元素的数组排序后,再重新组成上层数组
  3. 如此反复,直到大数组为止

在这里插入图片描述

package Sort;

import java.util.Arrays;
import java.util.Random;

import static Sort.SortExample.isSorted;
import static Sort.SortExample.less;

public class MergeSort {

    public static Comparable[] aux;

    // 主函数,供外界调用
    public static void sort(Comparable[] b){

        Comparable[] a = new Comparable[b.length];
        System.arraycopy(b, 0, a, 0, b.length);
        // System.out.println(Arrays.toString(b));

        aux = new Comparable[a.length];   // 1000
        sort(a, 0, a.length-1);          // 1000
    }

    // 通过递归将大数组不断划分,直到每个小数组只有两个元素
    public static void sort(Comparable[] a, int lo, int hi){
        // 将数组a[lo..hi] 排序
        if(hi <= lo)
            return;
        int mid = lo + (hi-lo)/2;  // 4
        sort(a, lo, mid);    // 左半边排序
        sort(a, mid+1, hi);  // 右半边排序
        merge(a, lo, mid, hi);   // 归并结果
    }
}

自底向上的归并排序

从最小数组开始排序,再排序较大数组,循环渐进地进行

public class MergeSortBU {

    public static void sort(Comparable[] b){

        Comparable[] a = new Comparable[b.length];
        System.arraycopy(b, 0, a, 0, b.length);

        // 进行 lgN 次两两归并
        int N = a.length;
        Comparable[] aux = new Comparable[N];
        for(int sz = 1; sz < N; sz *= 2){   // 以2个元素为一个最小数组
            for(int lo = 0; lo < N-sz; lo += sz+sz)     // 以相邻的两个小数组为一组进行归并
                merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));   // 将相邻的两个小数组归并

        if(!isSorted(a))
            System.out.println("自底向上归并排序失败");
        }
    }
}

特点:

  • 可以用来实现对链表的排序,只需要重新组织链表连接就能将链表原地排序

注意点:

  • 归并排序的空间复杂度不是最优的
  • 在实践中不一定会遇到最坏情况
  • 除了比较,算法的其他操作(例如:访问数组)也很重要
  • 不进行比较也能将某些数据排序

四种排序算法的比较

数据来源:algo4 官方提供的 algo4-data.zip 数据包

先进行1k数据量的比较:

选择排序,1000个数据,执行时间为:0.01177 s
插入排序,1000个数据,执行时间为:0.0137114 s
希尔排序,1000个数据,执行时间为:0.0014275 s
自顶向下归并排序,1000个数据,执行时间为:0.001443 s
自底向上归并排序,1000个数据,执行时间为:3.711E-4 s

进行32k数据量的比较:

选择排序,32000个数据,执行时间为:4.2373938 s
插入排序,32000个数据,执行时间为:2.6435673 s
希尔排序,32000个数据,执行时间为:0.0226827 s
自顶向下归并排序,32000个数据,执行时间为:0.0186592 s
自底向上归并排序,32000个数据,执行时间为:0.0120077 s

其他有关排序算法的文章:
选择排序,插入排序,希尔排序的详解与比较

快速排序算法详解与其他排序算法的比较

posted @ 2020-05-27 19:29  清澈的澈  阅读(8)  评论(0编辑  收藏  举报  来源