归并排序

懵懂的女人 提交于 2019-11-27 06:06:51

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

public static int[] mergeSort(int[] array) {
        // 后两个参数表示要进行归并排序的区间.
        // [0, array.length)
        // new 足够大的数组, 把这个数组作为缓冲区传给
        // 递归函数
        mergeSortHelper(array, 0, array.length);
        return array;
    }

    private static void mergeSortHelper(int[] array, int left, int right) {
        // [left, right) 构成了要去进行归并排序的区间
        // 如果区间为空区间, 或者只有一个元素, 都不用排序
        if (left >= right || right -  left == 1) {
            // 空区间或者区间只有一个元素, 都不需要进行归并排序
            return;
        }
        // 使用类似后序遍历的方式.
        // 先把当前的待排序区间拆成两半,
        // 递归的对这两个子区间进行归并排序, 保证两个区间有序之后
        // 再进行合并
        int mid = (left + right) / 2;
        // [left, mid)
        // [mid, right)
        mergeSortHelper(array, left, mid);
        mergeSortHelper(array, mid, right);
        merge(array, left, mid, right);
    }

    private static void merge(int[] array, int left,
                              int mid, int right) {
        // 创建一段临时空间辅助进行归并
        // 这个临时空间的长度应该是两个待归并区间的长度之和
        int length = right - left;
        int[] output = new int[length];
        // 这个变量保存着当前 output 中的末尾元素的下标
        int outputIndex = 0;
        // i 和 j 是用来遍历两个区间的辅助变量
        // [left, mid)
        // [mid, right)
        int i = left;
        int j = mid;
        while (i < mid && j < right) {
            // 此处的 if 条件必须要 <= , 否则没法保证稳定性
            if (array[i] <= array[j]) {
                // i 对应的元素比 j 小
                // 就把 i 对应的元素插入到 output 末尾
                output[outputIndex++] = array[i++];
            } else {
                output[outputIndex++] = array[j++];
            }
        }
        // 上面的循环结束之后, 两个区间至少有一个是遍历完了的.
        // 就把剩下的区间的内容直接拷贝到 output 中即可.
        while (i < mid) {
            output[outputIndex++] = array[i++];
        }
        while (j < right) {
            output[outputIndex++] = array[j++];
        }

        // 最后一步, 把 output 中的元素拷贝回原来的区间
        for (int k = 0; k < length; k++) {
            array[left + k] = output[k];
        }
    }

非递归:

public static int[] mergeSortByLoop(int[] array) {
        // 借助下标相关的规律来进行分组.
        // 初始情况下, 每个元素单独作为一组
        // [0] [1]    [2] [3]     [4] [5]
        // [0, 1] 和 [2, 3] 合并. [4, 5]  和 [6, 7] 区间合并
        // [0, 1, 2, 3]  [4, 5, 6, 7]
        for (int gap = 1; gap < array.length; gap *= 2) {
            for (int i = 0; i < array.length; i += 2 * gap) {
                // 这个循环负责在 gap 为指定值的情况下
                // 把所有的区间进行归并
                // 针对当前的 i, 也能划分出两个需要进行归并的区间
                // [beg, mid)
                // [mid, end)
                int beg = i;
                int mid = i + gap;
                int end = i + 2 * gap;
                if (mid > array.length) {
                    mid = array.length;
                }
                if (end > array.length) {
                    end = array.length;
                }
                merge(array, beg, mid, end);//上述递归中的方法
            }
        }
        return array;
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!