Java 归并排序

79 阅读3分钟

image.png 开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

前序

上一篇: 希尔排序

归并排序

理论

首先关于归并排序之前写的几个排序都不太一样。 归并: 将一组测试数据分割成两组然后对两组中的数据进行分别判断处理 ,然后讲两组数据在合并成一组数据,就是分而治之,也是 分治法 (Divide and Conquer)

分治法: 分土地 交公粮..... 商鞅变法——> 地主

动态演示图 :

mergeSort.gif

实践

coding

/***
 * mergeSort 归并排序测试
 */
@Test
public void mergeSortTest() throws Exception {
    int[] ints = {1, 3, 5, 8, 2, 2, 7, 7, 6, 88, 23, 56, 21, 15};
    System.out.println("原始数组数据");
    for (int i = 0; i < ints.length; i++) {
        System.out.print(ints[i] + ",");
    }
    long l = System.nanoTime();
    ints = mergeSort(ints);
    // 使用纳秒计算时间信息
    System.out.println("耗时" + (System.nanoTime() - l));
    System.out.println("排序之后的数据");
    for (int i = 0; i < ints.length; i++) {
        System.out.print(ints[i] + ",");
    }
}

/**
 *  排序
 * @param ints  初始数组
 * @return xx
 * @throws Exception
 */
public int[] mergeSort(int[] ints) throws Exception {
    if (ints.length >= 2) {
        int middle = ints.length / 2;
        int[] left = Arrays.copyOfRange(ints, 0, middle);
        int[] right = Arrays.copyOfRange(ints, middle, ints.length);
        //递归分治法
        // 首先 注意这块数中 对于自身数组 进行了递归排序 在最终merge 的时候数据已经是有序的了
        // 因为最后都是单个或者两个数进行比较的 分解因子是 / 2 
        return merge(mergeSort(left), mergeSort(right));
    } else {
        return ints;
    }
}

/**
 * 核心分治法
 *
 * @param left  左数组
 * @param right 右 数组
 * @return xx
 */
private int[] merge(int[] left, int[] right) {
    // 两个数组合并成一个
    int[] result = new int[left.length + right.length];
    int i = 0;

    //进行数据对比 进行数据排序交换
    // 这个算法 比较中 数组如果存在剩余的话 那么他其实就是剩下最大的数据集
    // 算法 示例   left:1356 right: 4789   right: 剩余7 8 9
    //  left: 4679  right: 1358  left 剩余 9
    while (left.length > 0 && right.length > 0) {
        //如果左边的小于右边的第一个 那么暂时将 临时数组的第一个暂时放左边的  ,
        //反之 将右边小的放在 临时数组的第一个
        // 然后一直 while 进行比较
        if (left[0] <= right[0]) {
            result[i++] = left[0];
            // 设置完成之后 将 原本  left数组的 0 位置的数据进行剔除 然后 进行重新 在后面进行数据比较
            left = Arrays.copyOfRange(left, 1, left.length);
        } else {
            result[i++] = right[0];
            // 设置完成之后 将 原本  rift数组的 0 位置的数据进行剔除 然后 进行重新 在后面进行数据比较
            right = Arrays.copyOfRange(right, 1, right.length);
        }
    }
    //上面会一直剔除 最后如果左边的还有数的话 就表明右边的数据全部都比较完成 然后呢 就剩下 左边还有数据 ,然后呢 右边的数据都比较完成了
    // 并且通过上面的排序 左右两边的数据都是已经有序了 左边还剩下的 就是左边都比临时数组大的 并且有序的数据  它有序是递归的时候已经进行了排序
    while (left.length > 0) {
        result[i++] = left[0];
        left = Arrays.copyOfRange(left, 1, left.length);
    }
    // 这个和上面的逻辑一样 右边剩下的也是 大的数据
    while (right.length > 0) {
        result[i++] = right[0];
        right = Arrays.copyOfRange(right, 1, right.length);
    }

    return result;
}

测试结果

image.png

总结

其实大家可以看见 所以整个归并排序来说它的数据效果对于之前前四篇的 效率不是很高,但是这个是因为我测试的数据量太小导致 的所以在实际情况下 ,分治法是一个很好的问题解决思路。 就好比我在这个算法里面有很多copy 数组的操作 ,这个是很耗时的,然后实际在开发过程中 可能不是这样的操作 ,然后呢,其实不知道大家有没有接触过 complatefuture 这个相关的高并发处理场景,这个流式计算其实也可以想想 也可以按照内容一个个执行处理 。

分治法: 这个是一个很重要的思想,分工而作,个人跟个人事,效率高。

image.png 这里说明一下,该算法中借用了多次临时数组的情况所以比较消耗内存以及时间 ,所以上图 OUT_PLACe

Ending

排序算法系列下一篇: 快速排序