【Java】基础排序算法-归并排序

【Java】基础排序算法-归并排序


前言

归并算法是分治法的一个典型的应用,其核心思想就是将两个有序的数组归并为一个更大的有序数组


实现过程(自顶向下)

在这里插入图片描述
“归”:将数组不断拆分,直到每个子数组段只包含一个元素;
“并”:不断地将相邻两个子数组段合并成一个大的有序数组,直到整个数组有序

时间复杂度:O(N*logN)
空间复杂度:O(N)
稳定性:稳定
归并方法实现:

归并过程的实现最直接的方法就是新创建一个大小合适的数组,将两个输入的数组段的元素一个个从小到大放到这个数组里去。

关键代码:(“归并”过程)
    //merger方法将两个有序子数组段arr[left...mid]和arr[mid+1...right]合并为一个更大的有序数组
    public static void merge(int[] arr, int left, int mid, int right) {
        int i = left;
        int j = mid + 1;
        //新建一个辅助数组暂存arr数组元素
        int[] aux = new int[right - left + 1];
        for (int k = 0; k < aux.length; k++) {
            //子数组段索引从left开始,需从left位置开始拷贝
            aux[k] = arr[k + left];
        }
        //遍历aux数组,此过程实现arr数组变有序
        for (int k = left; k <= right; k++) {
            //分别取i,j对应元素,较小值先存回数组arr
            // i > mid说明左边有序数组段已经处理完毕,取右边元素
            if (i > mid) {
                arr[k] = aux[j - left];
                j++;
            } else if (j > right) {
                // j > right 说明右边数组段处理完毕,取左边元素
                arr[k] = aux[i - left];
                i++;
            } else if (aux[i - left] > aux[j - left]) {
                //左边元素大于右边元素,取右边元素
                arr[k] = aux[j - left];
                j++;
            } else {
                //右边元素大于等于左边元素,取左边元素
                arr[k] = aux[i - left];
                i++;
            }
        }
    }
主代码:(递归地将数组拆分,然后归并)
    private static void mergeSortInternal(int[] arr, int left, int right) {
        //优化一:当right-left <= 15 即每个子区间长度小于等于15最好,
        // 此时用插入排序直接对每个子区间进行排序,会大大提高排序效率,减少拆分合并次数;
        if (right - left <= 15) {
            insertionShort(arr, left, right);
            return;
        }
//        // 归而为一过程,不断将数组拆分为只含一个元素的过程;用上面优化操作更好
//        // left == right说明数组已拆分为只含一个元素的数组
//        if (left == right) {
//            return;
//        }
		//将数组一分为二,mid表示左区间数组最后一个元素索引
        int mid = left + ((right - left) >> 1);
        //递归处理左区间
        mergeSortInternal(arr, left, mid);
        //递归处理右区间
        mergeSortInternal(arr, mid + 1, right);
        //优化二:
        //如果左区间最后一个元素小于右区间第一个元素,说明此时两数组段已经组成一个有序大数组
        //否则需要进行归并排序
        if (arr[mid] > arr[mid + 1]) {
            merge(arr, left, mid, right);
        }
    }
    //优化一:小区间内进行插入排序效率会大大提升
    private static void insertionShort(int[] arr, int left, int right) {
        for (int i = left + 1; i <= right; i++) {
            for (int j = i; j > left && arr[j] < arr[j - 1]; j--) {
                swap(arr, j, j - 1);
            }
        }
    }

优化一:实现上面的方法时并没有将数组拆分到只含有一个元素,而是拆分到每个子数组段大概包含15个元素左右,此时利用插入排序方法将子数组段直接排序,可以减少拆分合并次数,会大大提高排序效率;
优化二:在合并相邻子数组时,若左区间最后一个元素小于等于右区间第一个元素,说明此时两数组段已经组成了一个有序的大数组,此时不用执行归并过程。


归并排序非递归写法(自底向上):

在这里插入图片描述
自底向上排序时会多次遍历整个数组,根据子数组的大小进行两两合并,合并方法和上文相同,子数组初始大小值sz为1,每次加倍,最后一个子数组大小需要小于(数组长度为奇数时)或等于2*sz。

    public static void mergeSortNonRecursion(int[] arr) {
        for (int sz = 1; sz <= arr.length; sz += sz) {
            for (int i = 0; i + sz < arr.length; i += 2 * sz) {
                merge(arr, i, i + sz - 1, Math.min(i + 2 * sz - 1, arr.length - 1));
            }
        }
    }

内容如有任何不妥之处,恳请指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值