美文网首页
归并排序

归并排序

作者: 你大爷终归是你大爷 | 来源:发表于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++;
                }
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:归并排序

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