什么是归并排序?

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

算法描述

  • 把长度为n的输入序列分成两个长度为n/2的子序列;

  • 对这两个子序列分别采用归并排序;

  • 将两个排序好的子序列合并成一个最终的排序序列。

归并操作

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。

设有数列{6,202,100,301,38,8,1}

初始状态:6,202,100,301,38,8,1

第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;

第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;

第三次归并后:{1,6,8,38,100,202,301},比较次数:4;

总的比较次数为:3+4+4=11;

逆序数为14;

归并操作的原理

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针超出序列尾

将另一序列剩下的所有元素直接复制到合并序列尾

代码实现

public class MergeSort {
    private static int count = 1;
    public static void main(String[] args) {
        int[] arr = {8, 4, 5, 7, 1, 3, 6, 2, 9, 10};
        int[] temp = new int[arr.length];
        split(arr, 0, arr.length - 1, temp);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 递归拆分数组然后合并
     *
     * @param arr   待拆分数组
     * @param left  数组左边下标
     * @param right 数组右下标
     * @param temp  用于存放合并后的有序序列的数组
     */
    public static void split(int[] arr, int left, int right, int[] temp) {
        if (left >= right) {
            return;
        }
        System.out.println("拆分第"+(count++)+"次");
        int mid = left + (right - left) / 2;
        //向左拆分
        split(arr, left, mid, temp);
        //向右拆分
        split(arr, mid + 1, right, temp);
        //每次拆分后都执行合并
        merge(arr, left, mid, right, temp);
    }

    /**
     * 合并两个各自有序序列(以mid为界)
     *
     * @param arr   原始数组
     * @param left  数组左边下标
     * @param mid   数组中间下标
     * @param right 数组右边下标
     * @param temp  用于存放新的有序数组
     */
    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int i = left;
        int j = mid + 1;
        //temp中的原始下标
        int t = 0;

        while (i <= mid && j <= right) {
            //两边数组都没有比较完 继续
            if (arr[i] < arr[j]) {
                //左边数组中值更小
                temp[t] = arr[i];
                i++;
            } else {
                //右边数组中值更小
                temp[t] = arr[j];
                j++;
            }
            t++;
        }
        //有一边已经全部复制到temp中了
        if (i <= mid) {
            //左边还没有复制完,将左边全部元素复制到temp中
            while (i <= mid) {
                temp[t] = arr[i];
                i++;
                t++;
            }
        } else if (j <= right) {
            //右边还没有复制完,将右边全部元素复制到temp中
            while (j <= right) {
                temp[t] = arr[j];
                j++;
                t++;
            }
        }
        //将temp复制到原arr中
        t = 0;
        while (left <= right) {
            arr[left] = temp[t];
            left++;
            t++;
        }
    }
}

复杂度

归并排序把集合一层一层进行折半分组。如果集合长度是n,那么折半的层数就是logn,每一层进行归并操作的运算量是n。所以归并排序的时间复杂度等于每一层的运算量乘以层级数,即O(nlogn)=。
我们再看空间复杂度,由于每次归并所创建的的额外集合都会随着方法的结束而被释放,因此这部分空间不应该累计,单次归并操作开辟的最大的空间是n,所以归并排序的空间复杂度是O(n)。


总结

归并排序的执行原理,主要分为以下步骤:

1.递归的方式进行拆分,将大的数组拆分成小的数组,直到剩余一个不能拆分

2.回溯的时候进行合并,合并时以mid为界,左右两边各自有序,通过额外的空间temp数组,将两个有序数组合并到一个有序数组中

3.将合并后的数组复制到原数组中,当回溯完成时整个数组有序

推荐阅读更多精彩内容

  • 归并排序的归并这两个字和递归没有关系,归并是将两个有序的数组归并成一个更大的有序数组,但整个排序算法是有可能跟递归...
    我脱下短袖阅读 200评论 0 0
  • 一、对比分析图 均按从小到大排列 k代表数值中的"数位"个数 n代表数据规模 m代表数据的最大值减最小值 稳定性:...
    leo567阅读 573评论 0 1
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
    依依玖玥阅读 584评论 0 2
  • 排序的基本概念 在计算机程序开发过程中,经常需要一组数据元素(或记录)按某个关键字进行排序,排序完成的序列可用于快...
    Jack921阅读 1,056评论 1 4
  • 归并排序和快速排序都用到了分治思想,非常巧妙。我们可以借鉴这个思想,来解决非排序的问题。 归并排序 归并排序的核心...
    被吹落的风阅读 886评论 0 3