【每天进步一点点】归并排序(Merge Sort)

118 阅读1分钟

归并排序

归并排序是分治法中典型的应用

下文采用的是递归方式的归并排序,除此之外还有迭代法的归并排序

算法思路

将待排序的数组分成两部分,然后将左部分第一个元素和右部分第一个元素比较大小

小的元素拷贝到新数组,相等时,左边部分下标指向的数据拷贝到新数组

图示

merge sort2.gif

通过递归将数组拆成最小单元逐一比较,比较结束开始合并。

将一个数组拆分到合并的全过程如下图所示

merge sort1.jpg

代码部分

public class 归并排序 {
    public static void main(String[] args) {
        int[] arr = {5,2,4,8,9,6,2,5,3,2,1,4,7,8,5,3,6};
​
        /**
         * R 的值一定一定一定一定一定一定要记得是 arr.length - 1,要不然会数组越界!
         * 在最外层循环的时候会越界
         */
        process(arr,0,arr.length-1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "  ");
        }
    }
​
    public static void process(int[]arr,int L,int R) {
        if (L==R) {
            return;
        }
        int mid = L + ((R-L)>>1);
        process(arr, L, mid);
        process(arr, mid+1, R);
        merge(arr,L,mid,R);
    }
    
    /**
     * merge的过程实际上就是将原数组拆成一左一右,
     * 然后将两个数组的较小值放入一个新数组,最后将这个新数组拷贝回原数组返回
     */
    public static void merge(int[]arr,int L,int mid,int R) {
        int i = 0; // 新数组的下标
        int p1 = L; // 左数组的下标
        int p2 = mid+1; // 右数组的下标
        int[] newArr = new int[R-L + 1]; // 空数组,到时候赋值回原数组,注意长度不能是 arr.length
​
        while (p1<=mid && p2<=R) {
            newArr[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]; // 两个数字相等也先拷贝左边的数字会保证排序的稳定性
        }
​
        while (p1<=mid) {
            newArr[i++] = arr[p1++];
        }
​
        while (p2<=R) {
            newArr[i++] = arr[p2++];
        }
​
        for (int j = 0; j < newArr.length; j++) { // 不能用arr.length,要不然会让newArr数组越界
            arr[L + j] = newArr[j];
        }
    }
}

时间复杂度

O(nlogn)

排序稳定性

merge 的过程是具有稳定性的,因为相等的元素是先取左边的,相对次序没被打乱