归并排序

归并排序

要将一个数组排序,可以先(递归地)将他分成两半分别排序,然后将结果归并起来

归并排序能够保证将任意长度为 N N N 的数组排序所需 时间 N l o g 2 N Nlog_2N Nlog2N 成正比

主要缺点: 归并排序所需的 额外空间 N N N 成正比

自顶向下

对于长度为 N N N 的任意数组,自顶向下的归并排序需要 1 / 2 N l o g 2 N 1/2Nlog_2N 1/2Nlog2N N l o g 2 N Nlog_2N Nlog2N 次比较

对于长度为 N N N 的任意数组,自顶向下的归并排序最多需要访问数组 6 N l o g 2 N 6Nlog_2N 6Nlog2N

public class Merge {

    private static Comparable[] aux;

    public static void sort(Comparable[] a) {
        int length = a.length;

        aux = new Comparable[length];

        sort(a, 0, length - 1);
    }

    public static void sort(Comparable[] a, int lo, int hi) {
        if (hi <= lo) {
            return;
        }

        int mid = lo + (hi - lo) / 2;

        sort(a, lo, mid);

        sort(a, mid + 1, hi);

        merge(a, lo, mid, hi);
    }

    /**
     * 原地归并的抽象方法; 该方法将所有元素复制到 aux[] 中,然后在归并回到 a[] 中。 方法在归并时(第二个 for 循环)进行了 4 个条件判断: 左半边用尽 -> 取右半边的元素 右半边用尽 -> 取左半边的元素
     * 右半边的当前元素小于左半边当前元素 -> 取右半边的当前元素 右半边的当前元素大于等于左半边的当前元素 -> 取左半边的元素
     * 
     * @param a
     *            待排序数组
     * @param lo
     *            最小下标
     * @param mid
     *            中值下标
     * @param hi
     *            最大下标
     */
    private static void merge(Comparable[] a, int lo, int mid, int hi) {
        int i = lo;
        int j = mid + 1;

        // 将 a[lo...hi] 复制到 aux[lo...hi]
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k];
        }

        // 归并回到a[lo...hi]
        for (int k = lo; k <= hi; k++) {
            if (i > mid) {
                a[k] = aux[j++];
            } else if (j > hi) {
                a[k] = aux[i++];
            } else if (less(aux[j], aux[i])) {
                a[k] = aux[j++];
            } else {
                a[k] = aux[i++];
            }
        }

    }

    public static boolean less(Comparable a, Comparable b) {
        return a.compareTo(b) < 0;
    }
}

自底向上

算法原理:先归并微型数组,在***成对***归并得到的子数组

自底向上的归并排序多次遍历整个数组,根据子数组大小进行两两归并。子数组大小 sz 的初始值为 1,每次***加倍***。最后一个子数组的大小只有在数组大小时 sz 的***偶数倍***的时候才会等于 sz (否则它会比 sz 小)。

对于长度为 N N N 的任意数组,自底向上的归并排序需要 1 / 2 N l o g 2 N 1/2Nlog_2N 1/2Nlog2N N l o g 2 N Nlog_2N Nlog2N 次比较,最多访问数组 6 N l o g 2 N 6Nlog_2N 6Nlog2N 次。

当数组的长度为 2 的幂时,自顶向下和自底向上所用的比较次数和数组访问次数正好相同。

  • 自顶向下:化整为零
  • 自底向上:循序渐进
public class MergeBU {

    private static Comparable[] aux;

    public static void sort(Comparable[] a) {
        int length = a.length;

        aux = new Comparable[length];

        for (int sz = 1; sz < length; sz = sz + sz) {
            for (int lo = 0; lo < length - sz; lo += sz + sz) {
                merge(a, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, length - 1));
            }
        }
    }

    /**
     * 原地归并的抽象方法; 该方法将所有元素复制到 aux[] 中,然后在归并回到 a[] 中。 方法在归并时(第二个 for 循环)进行了 4 个条件判断: 左半边用尽 -> 取右半边的元素 右半边用尽 -> 取左半边的元素
     * 右半边的当前元素小于左半边当前元素 -> 取右半边的当前元素 右半边的当前元素大于等于左半边的当前元素 -> 取左半边的元素
     *
     * @param a
     *            待排序数组
     * @param lo
     *            最小下标
     * @param mid
     *            中值下标
     * @param hi
     *            最大下标
     */
    private static void merge(Comparable[] a, int lo, int mid, int hi) {
        int i = lo;
        int j = mid + 1;

        // 将 a[lo...hi] 复制到 aux[lo...hi]
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k];
        }

        // 归并回到a[lo...hi]
        for (int k = lo; k <= hi; k++) {
            if (i > mid) {
                a[k] = aux[j++];
            } else if (j > hi) {
                a[k] = aux[i++];
            } else if (less(aux[j], aux[i])) {
                a[k] = aux[j++];
            } else {
                a[k] = aux[i++];
            }
        }

    }

    public static boolean less(Comparable a, Comparable b) {
        return a.compareTo(b) < 0;
    }

}

归并排序的意义

归并排序是一种渐进最优的基于***比较排序***的算法

理解:最坏情况下任意基于***比较排序***的算法所需要的比较次数最少都是   N l o g 2 N ~Nlog_2N  Nlog2N

归并排序的最优性不是结束,也不代表实际应用中不需要考虑其他方法,因为:

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

理解:最坏情况下任意基于***比较排序***的算法所需要的比较次数最少都是   N l o g 2 N ~Nlog_2N  Nlog2N

归并排序的最优性不是结束,也不代表实际应用中不需要考虑其他方法,因为:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值