知识点11:常见的排序算法–归并排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dengminghli/article/details/75635379

相信看过常见的排序算法——快速排序的朋友们都记得,我们在介绍它的时候便阐述了使用快排的两种策略,分别是分治和递归。它的原理是:通过递归的方式,利用某个基底,不断将数列划分为更小的部分。直到小数组不能再拆分的时候,便已经完成了排序。而今天要介绍的归并排序,与它有很多相近的地方,不过也有很多的不同,让我们一起来探讨一下吧:

归并排序的原理

通过递归和分治的策略,以两个有序数组的归并为底板。将数列划分为若干有序个小数列,然后通过底板将其归并为一个已经排序的新数列。那么,我们如何确定两个数列是有序数列呢?那么试想,当小数列内部的元素为2时,它是否就已经可以进行排序,使其成为一个有序数组了呢?。不过,在这个问题上,有两种分歧。分别是:从一个数列开始,每次对半划分,直到出现有序序列后递归排序(自顶而上排序)和一次性将数列划分为元素为2的若干个有序序列,然后再进行递归。这两种方式实际上都是同样的,只是在分治理念上有点偏差。我们先来看一下我们的核心代码,也就是底板的实现:

/*
*两个有序数组的排序方法
*arr 数据源
*start 左边数组起始
*mid   左边数组结束
*end   右边数组结束
*/
public static void merge(int[] arr,int start,int mid,int end){
        int i = start;
        int j = mid+1;//右边数组起始
        int[] tmp = new int[end-start+1];
        int k = 0;
        for(k=start;k<=end;k++){
            tmp[k] =arr[k];
        }
        for(k=start;k<end;k++){
            if(i>mid){
                arr[k]=tmp[j++];
            }else if(j>end){
                arr[k] = tmp[i++];
            }else if(tmp[i]>tmp[j]){
                arr[k] = tmp[j++];
            }else{
                arr[k] = tmp[i++];
            }
        }
}

或许有人问:不是两个有序数组的排序吗?怎么只有一个数据源?这很简单,我们的最终目的是排序一个数组,所以我们最终也是在一个数组中模拟两个有序数组,并且进行归并,那么,用什么来划分呢?细看上面代码,你就发现啦。就是我们的索引。那么,如何确定我们的索引呢?这就要和实际情况相关了。话不多说,即可走起!

自顶而下的归并排序实现

/*
*自顶而下的归并排序实现
*arr 数据源
*start 排序起点
*end   排序终点
*/
public static void msort(int[] arr,int start,int end){
        if(start>=end){
        return ;
        }
        int mid = (end+start)/2;   //确定mid值(每次折半)
        msort(arr,start,mid);      //左边排序
        msort(arr,mid+1,end);      //右边排序
        merge(arr,start,mid,end); //调用底板排序
}

自底向上的归并排序实现

/*
*自底向上的归并排序实现
*arr 数据源
*/
public static void msort(int []arr){
        int size = a.length;
        int start,length;
        for(length=1;length<size;length*=2){
            for(start = 0;start <size-length;start+=length*2){
                //因为每次都是取2作为一个数组
                //同时每次取两个相邻数组进行排序,所以mid等于初始位置+一个数组的长度-1
                int mid = start+length-1;
                int end = (start+length*2-1)<(size-1)?start+length*2-1:size-1;
                merge(arr,start,mid,end);
            }
        }
}

总的来说,这两种方法其实各有利弊,前者易懂,但代码较多,运算速度可能不及后者。但后者代码晦涩难懂,用起来比较烧脑。所以,选择的时候,就要根据自己的实际理解情况啦。
排序算法系列结束
下一章:单例模式的实现

猜你喜欢

转载自blog.csdn.net/dengminghli/article/details/75635379