算法之归并排序/JAVA

0.3 归并排序

0.3.1 自顶向下的归并排序

算法思想:归并排序是分治思想的典型应用,归并排序又叫二路归并,是将一个数组从中间分成两个子数组,然后对子数组再归并,最后再将两个子数组进行合并。具体算法流程如下:

在实现归并排序的并的时候,有如下几种情况:

  • 左边小数组被用尽:取右边小数组元素进行并
  • 右边小数组被用尽:取左边小数组元素进行并
  • 左边数组元素小于右边数组元素:取左边小数组元素进行并
  • 右边数组元素小于左边元素:取右边小数组元素进行并

所以我们很容易就可以实现并的部分

	private static Comparable[] aux;
    

    public static void merge(Comparable[] a ,int low ,int mid ,int high){
        int i = low;
        int j = mid+1;
        //将a[low]-a[high]复制到临时数组aux
        for (int k = low; k < high; k++) {
            aux[k] = a[k];
        }
        //进行并
        for (int k = low; k <=high ; k++) {
            if (i > mid) {
                //左边用尽
                a[k] = aux[j++];
            } else if (j < high) {
                //右边用尽
                a[k] = aux[i++];
            } else if (less(aux[i], aux[j])) {
                //左比右小
                a[k] = aux[i++];
            } else {
                //右比左小
                a[k] = aux[j++];
            }
        }
    }

而从上方归并排序的图解中我们可以看出,用递归的方式进行分治是最简便的,所以我们归并排序的算法可以用以下代码实现。

public class Merge {
    private static Comparable[] aux;
    /**
     * 具体算法实现
     */
    public static void sort(Comparable[] a) {
        aux = new Comparable[a.length];
        sort(a,0,a.length-1);
    }

    public static void sort(Comparable[] a ,int low ,int high){
        //右小于左则不可分
        if (high <= low) {
            return;
        }
        //一分为二
        int mid =low + (high - low)/2;
        //左边再分
        sort(a ,low ,mid);
        //右边分
        sort(a ,mid+1 ,high);
        //治
        merge(a, low, mid, high);
    }



    public static void merge(Comparable[] a ,int low ,int mid ,int high){
        int i = low;
        int j = mid+1;
        //将a[low]-a[high]复制到临时数组aux
        for (int k = low; k <= high; k++) {
            aux[k] = a[k];
        }
        //进行并
        for (int k = low; k <=high ; k++) {
            if (i > mid) {
                //左边用尽
                a[k] = aux[j++];
            } else if (j > high) {
                //右边用尽
                a[k] = aux[i++];
            } else if (less(aux[i], aux[j])) {
                //左比右小
                a[k] = aux[i++];
            } else {
                //右比左小
                a[k] = aux[j++];
            }
        }
    }
    /**
     * 对两个元素进行比较,如果v<w返回true
     * @param v
     * @param w
     * @return
     */
    private static boolean less(Comparable v ,Comparable w){
        /**
         * compareTo 方法
         * 返回为正数表示v>w, 返回为负数表示v<w, 返回为0表示v==w
         */
        return v.compareTo(w) < 0;
    }

    /**
     * 交换a[i]和a[j]
     * @param a
     * @param i
     * @param j
     */
    private static void exch(Comparable[] a, int i, int j){
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    /**
     * 输出数组
     * @param a
     */
    private static void show(Comparable[] a){
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println();
    }

    /**
     * 测试数组是否有序
     * @param a
     * @return
     */
    private static boolean isSort(Comparable[] a){
        for (int i = 0; i < a.length; i++) {
            if (less(a[i],a[i-1])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 主函数
     * @param args
     */
    public static void main(String[] args) {
        String[] a = {"5","3","6","5","7","9","2","1","3","8"};
        sort(a);
        assert isSort(a);
        show(a);
    }

}

特点:

  • 时间复杂度:O(1/2NlgN)~O(NlgN)
  • 归并排序所需要的时间与NlgN成正比
  • 可以处理百万级大规模数组,因为其只需要遍历比整个数组多个对数因子的时间就能将一个庞大的数组排序
  • 需要辅助数组
  • 空间复杂度:O(1)
  • 是渐进最优的基于比较的排序算法,因为没有任何排序算法能够用少于~NlgN次比较将数组排序。
对于小规模数组中使用插入排序

上文我们说到,归并排序再大规模数组中具有优势,而选择排序或插入排序再小规模数组中的速度肯能超过归并排序,这是因为归并排序使用递归会使小规模问题中的方法调用过于频繁,所以我们可以考虑使用插入排序代替归并排序来进行效率的提高。

0.3.2 自底向上的归并排序

在理解了自顶向下的归并排序后,我们可以有这样的一种思考:我们可以直接将数组中的第0-1,2-3,4-5……进行归并,然后再将0-3,4-7……进行归并,以此类推,最后依然可以完成归并,这样就少了分治中的分的部分。同时这种方式的代码要精简的多。代码如下:

public class MergeLowToTop {
    private static Comparable[] aux;
    /**
     * 具体算法实现
     */
    public static void sort(Comparable[] a) {
        aux = new Comparable[a.length];
        //从1开始,进行lgN次归并
        for (int size = 1; size < a.length; size *= 2) {
            //确定每次并的最小数组
            for (int low = 0; low < a.length - size; low += 2*size) {
                //high要考虑最高是否会超过数组长度
                merge(a, low, low+size-1, Math.min(low+size*2-1, a.length-1));
            }
        }
    }
}

特点:

  • 相比于自顶向下,这种方式更适合于链表结构,因为每次只需要重新组织链表连接就是实现排序,而不用创建新的节点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高压锅码农777

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值