美文网首页
归并排序

归并排序

作者: 你大爷终归是你大爷 | 来源:发表于2021-05-08 11:15 被阅读0次

维护左右两部分分别有序,然后使用merge函数合并为整体有序,需要借助辅助数组空间。

算法复杂度:O(nlogn):相当于分成log n层的二叉树,每层复杂度为O(n)

归并算法中,要点,merge算法步骤(if有四个分支)
1、先算两边是否处理完毕,如左边处理完毕处理右边(看左右的索引值是否超过边界)
2、然后比较两边大小,用小的值赋值到对应位置,对应位置的索引加加

优化点
1、排序后,左边最大值比右边最小值要小,不进行归并(arr[mid].compareTo(arr[mid+1])<0),如果是有序数组,相当于每个叶子节段内部是常量操作O(1),叶子结点个数为复杂度,即为O(n)
2、递归后,小于一定值,使用插入排序(不确定起作用,和编译器环境有关)
3、归并算法每次都生成数组,可以在外面生成大数组,然后复用大数组,不用每次New

import java.util.Arrays;

/**
 * 归并排序
 * O(nlogn):相当于分成log n层的二叉树,每层复杂度为O(n)
 * 优化点
 * 1、排序后,左边最大值比右边最小值要小,不进行归并(arr[mid].compareTo(arr[mid+1])<0),
 *      如果是有序数组,相当于每个叶子节段内部是常量操作O(1),叶子结点个数为复杂度,即为O(n)
 * 2、递归后,小于一定值,使用插入排序(不确定起作用,和编译器环境有关)
 * 3、归并算法每次都生成数组,可以在外面生成大数组,然后复用大数组,不用每次New
 *      E[] arrTemp = Arrays.copyOf(arr,arr.length);
 *      System.arraycopy(arr,l,tempArr,l,r-l+1);
 *
 * 归并算法中,要点,merge算法步骤(if有四个分支)
 * 1、先算两边是否处理完毕,如左边处理完毕处理右边(看左右的索引值是否超过边界)
 * 2、然后比较两边大小,用小的值赋值到对应位置,对应位置的索引加加
 */
public class MergeSort {
    public static <E extends Comparable<E>> void sort(E[] arr) {
        if (arr == null) {
            return;
        }
        sort(arr,0, arr.length-1);
//        merge(arr,0, arr.length/2, arr.length-1);
    }

    // 区间为前闭后闭
    private static <E extends Comparable<E>> void sort(E[] arr,int l,int r){
        if (l>=r) {
            return;
        }

        // 优化点
        // 因merge和sort内部 操作较多(复杂度的常量),
        // 在数组长度比较小的情况,不如简单的插入排序
        // java有效(该优化牵扯方法切换,可能在其他脚本语言会更耗时,暂注释掉)
//        if (r-l<=15) {
//            InsertionSort.sort2(arr,l,r);
//            return;
//        }
        // 分成前后两段
        int mid = (l+r)/2;
        sort(arr,l,mid);
        sort(arr,mid+1,r);

        // 优化点
        // 1、如果是有序队列,可以使算法复杂度变为O(n)
        // 2、因为内部循环不执行,相当于只执行递归,复杂度计算,相当于求二叉树的节点数
        // 从底向上加为 (1/2+1/4+1/8+...)*n ≈ O(n)
        ///3、参考插入排序法遇到有序数组情况,也为O(n)
        if (arr[mid].compareTo(arr[mid+1])>0) {
            merge(arr, l, mid, r); // 两段有序数组排序
        }
    }

    // merge原则:左边(有序) 和 右边(有序) 比较 ,小的放入数组第K个元素
    private static <E extends Comparable<E>> void merge(E[] arr,int l,int mid,int r) {
        E[] tempArr = Arrays.copyOfRange(arr, l, r + 1);
        int i=l,j=mid+1; // 左右两边的下标,和 主Arr 相差 l;
        for (int k = l; k <= r; k++) {
            if (i>mid) { // 左边已经添加完毕,只操作右边
                arr[k] = tempArr[j-l];
                j++;
            } else if (j>r) { // 右边已经添加完毕,只操作左边
                arr[k] = tempArr[i-l];
                i++;
            } else if (tempArr[i-l].compareTo(tempArr[j-l])<0) { // 比较 左右 两边 哪个小
                arr[k] = tempArr[i-l];
                i++;
            } else {
                arr[k] = tempArr[j-l];
                j++;
            }
        }
    }
}

优化

import java.util.Arrays;

public class MergeSort {

    // 优化 使用外部数组,减少声明空间次数
    public static <E extends Comparable<E>> void sort2(E[] arr) {
        if (arr == null) {
            return;
        }
        E[] arrTemp = Arrays.copyOf(arr,arr.length);
        sort2(arr,0, arr.length-1,arrTemp);
//        merge(arr,0, arr.length/2, arr.length-1);
    }

    // 区间为前闭后闭
    private static <E extends Comparable<E>> void sort2(E[] arr,int l,int r,E[] arrTemp){
        if (l>=r) {
            return;
        }

        // 优化点
        // 因merge和sort内部 操作较多(复杂度的常量),
        // 在数组长度比较小的情况,不如简单的插入排序
        // java有效(该优化牵扯方法切换,可能在其他脚本语言会更耗时,暂注释掉)
//        if (r-l<=15) {
//            InsertionSort.sort2(arr,l,r);
//            return;
//        }
        // 分成前后两段
        int mid = (l+r)/2;
        sort2(arr,l,mid,arrTemp);
        sort2(arr,mid+1,r,arrTemp);

        // 优化点
        // 1、如果是有序队列,可以使算法复杂度变为O(n)
        // 2、因为内部循环不执行,相当于只执行递归,复杂度计算,相当于求二叉树的节点数
        // 从底向上加为 (1/2+1/4+1/8+...)*n ≈ O(n)
        ///3、参考插入排序法遇到有序数组情况,也为O(n)
        if (arr[mid].compareTo(arr[mid+1])>0) {
            merge2(arr, l, mid, r,arrTemp); // 两段有序数组排序
        }
    }

    // merge原则:左边(有序) 和 右边(有序) 比较 ,小的放入数组第K个元素
    private static <E extends Comparable<E>> void merge2(E[] arr,int l,int mid,int r,E[] tempArr) {
        //E[] tempArr = Arrays.copyOfRange(arr, l, r + 1);
        System.arraycopy(arr,l,tempArr,l,r-l+1);
        int i=l,j=mid+1; // 左右两边的下标,和 主Arr 相差 l;
        for (int k = l; k <= r; k++) {
            if (i>mid) {
                arr[k] = tempArr[j];
                j++;
            } else if (j>r) {
                arr[k] = tempArr[i];
                i++;
            } else if (tempArr[i].compareTo(tempArr[j])<0) { // 比较 左右 两边 哪个小
                arr[k] = tempArr[i];
                i++;
            } else {
                arr[k] = tempArr[j];
                j++;
            }
        }
    }
}

相关文章

  • 排序算法

    约定 选择排序 冒泡排序 插入排序 希尔排序 归并排序1. 归并方法2. 自顶向下归并排序3. 自底向上归并排序 ...

  • 排序二:归并、快排

    文章结构 归并排序 快速排序 源码 1. 归并排序 1.1 什么是归并排序 归并排序的思想是:将待排序的区间平分成...

  • java归并排序

    归并排序什么是归并排序:图解归并排序归并排序有两种实现方式,一是基于递归,而是基于迭代1)基于递归的归并排序: 基...

  • 算法—排序篇2

    1、归并排序(Merging Sort) 归并排序(Merging Sort): 就是利用归并的思想实现排序⽅法....

  • 常见的排序算法(2)

    要点 快速排序 归并排序 1.快速排序 2.归并排序

  • 排序算法之归并排序

    归并排序(Merge Sort) 归并排序是利用归并的思想实现排序的方式,该算法采用的是经典的分治算法 归并排序过...

  • 算法 第二章第二部分笔记

    各种排序算法的性能特点 选择排序 插入排序 希尔排序 归并排序 本地归并排序 自底向上的归并排序 快速排序 三向切...

  • 归并排序(二路归并排序)

    归并排序的思路 归并排序是通过“归并”操作完成排序的,将两个或者多个有序子表归并成一个子表。归并排序是“分治法”的...

  • 算法排序之归并排序和快速排序

    归并排序和快速排序用的都是分治的思想,用递归的编程技巧来实现.咱们先来看归并排序. 归并排序 归并排序的核心思想就...

  • 基于左闭右开的乱序数组归并排序 2020-04-24(未经允许,

    归并排序代码模板 递归形式思路:二分nums数组后对nums的归并排序 = 对左侧数组归并排序+对右侧数组归并排序...

网友评论

      本文标题:归并排序

      本文链接:https://www.haomeiwen.com/subject/ytfedltx.html