一、原理

​ 归并算法是采用分治法(Devide and Conquer)的一个典型应用,即先保证每个子序列有序,再将子序列进行合并,处理使得合并后的子序列有序,如此逐步得到一个有序的序列。

​ 归并算法在实现上分为自下向上的迭代归并排序和自上向下的递归式的归并排序。下面将分别以代码形式实现两种实现方法。

二、代码实现

2.1 迭代实现方式的 java代码实现

package com.java;

public class MergeSortIterate {
    public static void main(String[] args){
        int[] arrs = {15,7,30,28,6,66,43,3,70,5};
        System.out.println("归并排序前:");
        display(arrs);
        mergeSort(arrs);
        System.out.println("归并排序后:");
        display(arrs);
    }

    public static void mergeSort(int[] arrs) {
        int[] temp = new int[arrs.length];//临时空间,用于存放归并结果
        int squa = 1;//子序列起始长度为1
        while(squa < arrs.length){
            mergeDownToUp(arrs,temp,squa,arrs.length);
            squa = 2 * squa;
        }
    }

    public static void mergeDownToUp(int[] arrs, int[] temp, int squa, int length) {
        int left = 0,middle,right;

        while (left < length -2*squa + 1){//即right<length  length-right = length-2*squa+1 > left
            right = left + 2*squa -1;//即right的值是left的值加上两个相邻子组的长度减一
            middle = (left + right)/2;
            merge(arrs,temp,left,middle,right);
            left = left + 2*squa;
        }

        //处理剩下的尾数
        if(left < length - squa + 1){ //即middle<length 推导(length -middle = length - squa + 1 > left)
            merge(arrs,temp,left,left+squa-1,length-1);
        }else{
            while(left < length){
                if(temp[left] != arrs[left]){
                    temp[left] = arrs[left];
                }
                left = left + 1;
            }
        }
    }

    public static void merge(int[] arrs, int[] temp, int start, int middle, int end) {
        int i = start,t = start,j = middle + 1;
        while (i <= middle && j<=end){
            if(arrs[i] <= arrs[j]){
                temp[t] = arrs[i];
                t = t + 1;
                i = i + 1;
            }else{
                temp[t] = arrs[j];
                t = t + 1;
                j = j + 1;
            }
        }

        while (i <= middle){//将前半部分剩余的复制到temp
            temp[t] = arrs[i];
            t = t + 1;
            i = i + 1;
        }

        while (j <= end){//将后半部分剩余的复制到temp
            temp[t] = arrs[j];
            t = t + 1;
            j = j + 1;
        }
        while (start <= end){//将temp的数据覆盖arrs的数据
            if (arrs[start] != temp[start]){
                arrs[start] = temp[start];
            }
            start = start + 1;
        }
    }

    public static void display(int[] arrs){
        for(int i = 0;i < arrs.length;i++){
            System.out.print(" " + arrs[i] + " ");
        }
        System.out.println();
    }
}

运行结果如下:

归并排序前:
 15  7  30  28  6  66  43  3  70  5 
归并排序后:
 3  5  6  7  15  28  30  43  66  70 

2.2 递归实现方式的 java代码实现

package com.java;

public class RecursiveSort {

    public static void main(String[] args){
        int[] arrs = {15,7,30,28,6,66,43,3,70,5};
        System.out.println("归并排序前:");
        display(arrs);
        mergeSort(arrs);
        System.out.println("归并排序后:");
        display(arrs);
    }


    public static void mergeSort(int[] arrs) {
        int []temp = new int[arrs.length];//临时空间,用于存放归并结果
        mergeUpToDown(arrs,0,arrs.length-1,temp);
    }

    private static void mergeUpToDown(int[] arr,int left,int right,int []temp){
        if(left<right){
            int mid = (left+right)/2;
            mergeUpToDown(arr,left,mid,temp);//对左边进行归并排序,使得左子序列有序
            mergeUpToDown(arr,mid+1,right,temp);//对右边进行归并排序,使得右子序列有序
            merge(arr,temp,left,mid,right);//将两边的有序子数组进行合并
        }
    }

    public static void merge(int[] arrs, int[] temp, int start, int middle, int end) {
        int i = start,t = start,j = middle + 1;
        while (i <= middle && j<=end){
            if(arrs[i] <= arrs[j]){
                temp[t] = arrs[i];
                t = t + 1;
                i = i + 1;
            }else{
                temp[t] = arrs[j];
                t = t + 1;
                j = j + 1;
            }
        }

        while (i <= middle){//将前半部分剩余的复制到temp
            temp[t] = arrs[i];
            t = t + 1;
            i = i + 1;
        }

        while (j <= end){//将后半部分剩余的复制到temp
            temp[t] = arrs[j];
            t = t + 1;
            j = j + 1;
        }
        while (start <= end){//将temp的数据覆盖arrs的数据
            if (arrs[start] != temp[start]){
                arrs[start] = temp[start];
            }
            start = start + 1;
        }
    }

    public static void display(int[] arrs){
        for(int i = 0;i < arrs.length;i++){
            System.out.print(" " + arrs[i] + " ");
        }
        System.out.println();
    }

}

运行结果如下:

归并排序前:
 15  7  30  28  6  66  43  3  70  5 
归并排序后:
 3  5  6  7  15  28  30  43  66  70 

三、复杂度分析

3.1 时间复杂度分析

​ 归并排序类似于遍历二叉树,其需要遍历的次数即是二叉树的深度。由此可知归并排序的时间复杂度是O(n*log2n)。

3.2 空间复杂度分析

​ 空间复杂度:空间复杂度是运行完一个程序所需要的内存的大小。即包括了存储算法本身所需要的空间,输入与输出数据所占空间,以及一些临时变量所占用的空间。一般而言,只比较额外空间,来比较算法的空间优越性。

​ 由上述代码可知,归并排序需要一个长度和待排序数组一样长度的数组来存放临时数据,因此其空间复杂度是O(n)。

四、稳定性

归并排序中相等的元素不会改变位置,因此归并算法是稳定的算法。

posted on 2019-07-31 20:52  IT-飞鹰  阅读(193)  评论(0编辑  收藏  举报