【排序】归并排序

92 阅读2分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

归并排序

归并排序(MERGE-SORT) 是利用归并的思想实现的排序方法, 该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解, 而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起, 即分而治之)。

1) 整体就是一个简单递归, 左边排好序、 右边排好序、 合并左边和右边让其整体有序

2) 让其整体有序的过程里用了外排序方法

思路

1.主方法merSort将原来的部分分成左边部分和右边部分,对这两部分进行递归,递归完成后进行merge合并操作

2.merge操作将两个部分的的元素排序好写入辅助数组,完成后再将辅助数组中的值拷贝到原数组的对应位置

代码

   public void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) return;
        mergeSort(arr, 0, arr.length - 1);
    }

    public void mergeSort(int[] arr, int L, int R) {
        if (L == R) return;
        int mid = L + ((R - L) >> 1);
        mergeSort(arr, L, mid);
        mergeSort(arr, mid + 1, R);
        merge(arr, L, R, mid);

    }

    public void merge(int[] arr, int L, int R, int M) {
        int[] temp = new int[R - L + 1]; //辅助数组
        int i = 0; //变量i给辅助数组使用
        int p1 = L;
        int p2 = M + 1;
        while (p1 <= M && p2 <= R) {
            temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= M) {
            temp[i++] = arr[p1++];
        }
        while (p2 <= R) {
            temp[i++] = arr[p2++];
        }
        //将结果拷贝到原数组中
        for (int j = L; j <= R; j++) {
            arr[j] = temp[j - L];
        }
    }

时间复杂度

O (N * logN)

Master公式

对递归的时间复杂度计算涉及到Master公式(Master公式是用来解决递归问题时间复杂度的公式)

记录主方法的表现形式:

T [n] = aT[n/b] +O(N^d) 1

  • ①当d<logb a时,时间复杂度为O(n^(logb a))
  • ②当d=logb a时,时间复杂度为O((n^d)*logn)
  • ③当d>logb a时,时间复杂度为O(n^d)

其表示的意义是n表示问题的规模,a表示递归的次数也就是生成的子问题数,b表示每次递归是原来的1/b之一个规模,f(n)表示分解和合并所要花费的时间之和。

然后我们来分析一下归并排序的时间复杂度: a=2 每次生成的子问题数为2 b= 2 每次递归是原来1/2的规模 d=1 递归过程中除了递归部分其他部分使用的时间为O(N)

所以此时d= logb a 时间复杂度为O((n^1) * logn)=O (N * logN)

为什么时间复杂度能够达到O(N *logN)

简单排序为什么是O(N^2),因为浪费了大量的比较行为,遍历1次只搞定了一个数,每一轮的比较都是独立,前面的比较信息被丢弃了但是归并排序没有浪费比较行为,比较行为信息被留下来了变成了整体有序的部分,比较信息实际通过已经排序好的部分向上传递给将要排序的部分。

额外空间复杂度

递归过程中merge方法使用到辅助数组,使用完之后回收,数组的最大长度等于原数组的长度,所以额外空间复杂度为O(N)