递归与分治策略-2.7归并排序及其优化总结

引言:众所周知,归并排序(合并排序)算法是基于分治策略的一个排序算法,其基本思想是:将待排序元素分成大小大致相同的2个子集合,分别对2个子集合进行排序,最终将排序好的子集合合并成为所要求的排好序的集合。具体过程如下图所示(动图源于网络):
《递归与分治策略-2.7归并排序及其优化总结》

如图所示:归并排序需要三个索引分别是ijk。其中i指向带排序第一个子集合待比较元素,j指向待排序另一个子集合待比较元素,k指向数组b中的元素。
《递归与分治策略-2.7归并排序及其优化总结》
递归代码如下:

public static void mergeSort(int a[],int l,int r){
        if(l>=r)   return;    //递归尾->至少有两个元素
        int m = (l+r)/2;
        mergeSort(a,l,m);     //类似细胞分解,左半
        mergeSort(a,m+1,r);   //类似细胞分解,有半
        merge(a,b,l,m,r);     //将a数组排好序合并到b数组里,最后复制回a 
    }
    public static void merge(int a[],int b[],int l,int m,int r){
        //1.开始归并
        int i=l,k=l,j=m+1;
        while(i<=m&&j<=r){
            if(a[i]<=a[j]){   //这是归并排序是稳定性排序的关键条件,若取严格不等号,则不稳定
                b[k++] = a[i++];
            }else  b[k++] = a[j++];
        }
        //2.对结尾元素进行处理
        if(i<=m){     
            for(int q=i;q<=m;q++)
              b[k++] = a[q];
        }else{
            for(int q=j;q<=r;q++)
              b[k++] = a[q];
        }
        //3.复制回数组a
        for(int q=l;q<=r;q++)
            a[q] = b[q];
    }

运行结果如下:

初始数组:49 38 65 97 76 13 27 
排序数组:13 27 38 49 65 76 97 

读者可以发现在mergeSort递归算法里面,每一次都要进入merge方法进行合并,可事实并不是每一次都需要。比如归并的两部分数组拼接到一起就已经是成序的数组或者输入的数组就已经是有序的数组,这样就不需要再进一步合并。因此可以在此前加一个判断即可减少一定的归并次数
优化代码如下:

public static void mergeSort(int a[],int l,int r){
        if(l>=r)   return;    //递归尾->至少有两个元素
        int m = (l+r)/2;
        mergeSort(a,l,m);     //类似细胞分解,左半
        mergeSort(a,m+1,r);   //类似细胞分解,有半
        if(a[m]>a[m+1])
           merge(a,b,l,m,r);  //将a数组排好序合并到b数组里 
    }

以上通过判断第一部分数组的最后一个元素是否小于另一部分数组第一个元素来实现,即加上if(a[m]>a[m+1])判断。因为两部分数组已经各自成序,只要判断其边缘元素大小即可。

补充:以上只讨论了归并排序的递归实现,是自顶向下的,实际上还有迭代法的实现,即自底向上。迭代法程序比递归法略长,但特点是可无需向数组中通过索引读取元素,这使得迭代法可用在数据结构是链表的情况下进行排序。

    原文作者:递归与分治算法
    原文地址: https://blog.csdn.net/SL_World/article/details/78172397
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞