经典排序之归并排序详解O(nlog2n)

经典排序之归并排序详解

@Desciption: 归并排序

归并:把两个或多个有序的序列合并成一个

算法思想:就是先将一个 代排序的数组递归成 一个 一个 的元素 然后在分别两两合并 然后合并完 就成了 一些 有序数组 然后在继续 两两合并这个两个有序数组… 直到合并完毕 那么带排序的就变成了 有序数组了。

算法效率分析:
1、时间复杂度O(nlog2n)
分析 : 由于此算法为 归并的趟数 * 每次合并元素的数 * 2路“归并树”—形态上为一刻倒立的二叉树 那么 趟数就位 log2n 级别的 * 每次的合并
即当与在二叉树每一层对比某两个有序序列的元素 那么每一层总的对比肯定小于等于n-1 * 那么每一趟的合并时间复杂为O(n)级别的
那么此算法时间复杂度为 O(nlog2n)

2、空间复杂度O(n),来自于merger方法的辅助数组help[]
分析:由于这里运用了递归的方式实现的,这里的递归栈也使用了一些辅助空间,但是它不会超过log2n级别的 * 而辅助数组是n级别的 因此
最终取高阶 空间复杂度即为O(n)级别的

稳定性:稳定的
分析:在merge方法里合并的时候,如果遇到两个关键字相等的时候,会优先将左边相等的放进辅助数组里 * 因此归并排序是稳定的算法。

动画演示
在这里插入图片描述

代码:

package zcy_MergeSort;

import org.junit.Test;

/**
 * @author 米兰的小铁匠
 * @date 2022/4/10/010 11:31
 * @Desciption: 归并排序
 * 归并:把两个或多个有序的序列合并成一个
 * 算法思想:就是先将一个 代排序的数组递归成 一个 一个 的元素 然后在分别两两合并
 * 然后合并完 就成了 一些 有序数组 然后在继续 两两合并这个两个有序数组... 直到合并完毕
 * 那么带排序的就变成了 有序数组了。
 *
 * 算法效率分析:
 *
 * 1、时间复杂度: O(nlog2n)
 * 分析 : 由于此算法为 归并的趟数 * 每次合并元素的数
 * 2路“归并树”---形态上为一刻倒立的二叉树 那么 趟数就位 log2n 级别的
 * 每次的合并 即当与在二叉树每一层对比某两个有序序列的元素 那么每一层总的对比肯定小于等于n-1
 * 那么每一趟的合并时间复杂为O(n)级别的
 * 那么此算法时间复杂度为 O(nlog2n)
 *
 * 2、空间复杂度:O(n),来自于merger方法的辅助数组help[]
 * 分析:由于这里运用了递归的方式实现的,这里的递归栈也使用了一些辅助空间,但是它不会超过log2n级别的
 * 而辅助数组是n级别的 因此 最终取高阶 空间复杂度即为O(n)级别的
 *
 * 稳定性:稳定的
 * 分析:在merge方法里合并的时候,如果遇到两个关键字相等的时候,会优先将左边相等的放进辅助数组里
 * 因此归并排序是稳定的算法。
 */
@SuppressWarnings("all")
public class MergeSort {

    //递归方法实现
    public static void mergeSort1(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        process(arr, 0, arr.length - 1);
    }

    public static void process(int arr[], int L, int R) {
        //如果递归到 L和R指向一样的 就代表递归结束 返回上一层递归函数。
        if (L == R) {
            return;
        }
        int mid = (L + R) >> 1;      //mid取中间的,注意这里是向下取整,如果mid=2.5 那么java会向下取整为2
        process(arr, L, mid);        //对左半部分进行排序
        process(arr, mid + 1, R);  //对右半部分进行排序
        merge(arr, L, mid, R);       //归并两个有序序列
    }

    public static void merge(int arr[], int L, int M, int R) {
        int[] help = new int[R - L + 1];//定义辅助数组为两个有序序列的长度-此长度即为R-L+1
        int i = 0;
        int p1 = L;
        int p2 = M + 1;

        /*
         * 对这两个有序序列进行归并
         * 分别从两个有序序列的第一个开始往后比较,谁小就把谁放进辅助数组,然后往后移动
         * 当移动到一个有序序列越界时,就代表一个有序序列已经整个放进辅助数组里面了。
         * 然后while循环结束
         * */
        while (p1 <= M && p2 <= R) {
            /*
             * 这里 假如如果左边的小于等于右边的就把左边放进辅助数组里面去,否则放右边的
             * 也就是 只有当右边小于左边的,才把右边的放进辅助数组里面去。
             * 这样就保证了排序的稳定性
             * */
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }

        //如果p1小于等于M,说明P1没有完全放进辅助数组,而P2完全放进去了,那么就把P1剩下的放进辅助数组
        while (p1 <= M) {
            help[i++] = arr[p1++];
        }

        //如果p2小于等于R,说明P2没有完全放进辅助数组,而P1完全放进去了,那么就把P2剩下的放进辅助数组
        while (p2 <= R) {
            help[i++] = arr[p2++];
        }

        //最后将辅助数组元素全部复制到arr数组里面去
        for (i = 0; i < help.length; i++) {
            arr[L + i] = help[i];
        }
    }
}

测试:

//测试
    @Test
    public void test() {
        int[] arr = {1,4,6,2,7,43,2,0,21};
        System.out.println("-------排序前---------");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println("");
        System.out.println("-------排序后---------");
        mergeSort1(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

运行结果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值