排序算法整理小结(归并排序)

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

     排序算法,说真的,就是一个排列顺序的算法,有些废话了哈,不过话说回来,顺序对于我们实现某些功能有很大的帮助,然而如何能够快速的将一个无序数列排列好呢?这次整理了几种排序方法,总结讲述了一下实现的原理以及过程,话比较直接,相信各位看官看代码也许比我说要好得多,直接开始吧。

1.归并排序

    单单看这个名字,归并,归并什么呢,当然是归并我们要排列的数列,或者某个含有顺序的内容串,那为什么要归并呢,因为他们分开了呀,为什么分开了呀?简单一点说就是为了方便比较以及处理。如果我们能理解好这个过程,其是归并排序我们也就大体了解了大部分内容了,只不过我们需要将两个过程细化,用代码实现,来上代码:

public class MergeSort {
    public static void mergeSort(int[] array, int left, int right){

        // 此递归过程的出口为,right - left < 2 时,此时相当于两者之间仅有
        // 一个元素
        if(right - left < 2) return ;

        //计算mid值
        int mid = (left + right) >> 1;

        //归并的“分开”过程,相当于不停的对左半边的数据进行对半分开
        mergeSort(array,left,mid);
        //归并的“分开”过程,相当于不停的对右半边的数据进行对半分开
        mergeSort(array,mid , right);
        //归并的合并过程,将分开的排序合并
        merge(array,left,mid,right);
    }

    private static void merge(int[] array, int left, int mid, int right) {

        //相关位置2:此位置 i 初始值为 left的值,j 的初始值为mid的值
        int i = left, j = mid, k = 0;
        int [] temp = new int [right - left];

        //相关位置1,在这里我们是令i < mid, j < right;注意,我们这里
        //使用的是<,不是<=,所以当使用<的时候,也就是说i最大到i - 1
        //j 最大到right - 1


        /**
         * 此部分进行排序输出到temp数组中,相当于用mid将整个数组分为
         * 两个数组,从两个数组中分别选取符合条件的元素,插入到
         * temp数组中
         */
        for(; i < mid && j < right; k++){
            if(array[i] < array[j]){
                temp[k] = array[i++];
            }else{
                temp[k] = array[j++];
            }
        }

        //可以看到上面的for循环只要有一个到达末尾就停止了,所以,
        //分成的两个数组大小不一样时,就容易产生这样的情况,所以
        //需要将剩余的部分输入到temp结果数组中
        for(; i < mid; k++){
            temp[k] = array[i++];
        }

        for(; j < right; k++){
            temp[k] = array[j++];
        }

        //将某次排序的结果重新拷贝回原来的数组
        for (i = left, k = 0; i < right; i++){
            array[i] = temp[k++];
        }
    }

    /**
     * 打印函数
     * @param array 被打印数组
     */
    public static void print(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.println("element : " + array[i]);
        }
    }
}

     分析一下可以看到,每次排序所花费的事件为lgn,所以此方法排序所用的时间的O(nlgn)。对于归并排序来说重要的内容为归并和关键位置,即边界的准确确定,这样能够保证算法的基本功能能够实现,其次控制好递归,理解递归所表述的过程,这样就能顺利的理解归并排序了。参考的有些资料说,某些情况下,我们可以结合插入排序达到排序速度提高的目的,当然前提是归并排序中将数组划分到一定范围内时,此时若采用插入排序,效果会比上面的归并排序算法要好,那我们怎么设计呢,仔细思考一下其是还是有思路的,我们要替换的为元素个数小于一个特定值时,我们采用插入排序,其是对应的为止为我们的递归的出口,也就是说当我们划分到小于这个特定值时,就要执行插入排序了,同时,还要保证归并的时候其他部分继续使用merge,所以我们进行如下设计:

   //阈值,当元素数量小于此值时,将进行insert排序
    private static final int INSECTION_SORT_THERHOLD = 20;

    /**
     *merge 部分不变,修改mergesort部分
     */ 
    public static void mergeSort_mix_isnertion(int[] array, int left, int right){

        // 此递归过程的出口为,right - left < 2 时,此时相当于两者之间仅有
        // 一个元素
        if(right - left < 2) return ;

        if(right - left < INSECTION_SORT_THERHOLD){
            insertion_sort(array,left,right);
        }else{
            //计算mid值
            int mid = (left + right) >> 1;

            //归并的“分开”过程,相当于不停的对左半边的数据进行对半分开
            mergeSort(array,left,mid);
            //归并的“分开”过程,相当于不停的对右半边的数据进行对半分开
            mergeSort(array,mid , right);
            //归并的合并过程,将分开的排序合并
            merge(array,left,mid,right);
        }

    }

    /**
     *insertion排序方法
     */
    public static void insertion_sort(int[] array, int left, int right){
        int i, key;

        for(int j = left + 1; j < right; j++){
            key = array[j];
            i = j - 1;
            while (i >= left && array[i] > key){
                array[i + 1] = array[i];
                i = i - 1;
            }
            array[i + 1] = key;
        }
    }

测试结果如下:

    public static void main(String[] args){
        int numItems = 10000000;
        int[] array1 = new int[numItems];
        int[] array2 = new int[numItems];
        int i = 37;

        for (i = 37; i != 0; i = (i + 37) % numItems) {
            array1[i] = i;
            array2[i] = i;
        }

        long starttime2 = System.currentTimeMillis();
        //在这里说一下关于array.length和array.length - 1区别:在这里的赋值
        //其实和后面调用的函数有关,有关的位置已经标识。
        mergeSort(array2,0,array2.length);
        long endtime2 = System.currentTimeMillis();
        System.out.println("mergeSort used time" +(endtime2 - starttime2));


        long starttime = System.currentTimeMillis();
        mergeSort_mix_isnertion(array1,0,array1.length );
        long endtime = System.currentTimeMillis();
        System.out.println("mergeSort_mix_insection used time" + (endtime - starttime));
    }
}

在这里随机生成了array1和array2两个数组,都有100000000个数字,并分别用两个方法进行排序,将阈值设置为20时,运行时间分别为:

单位为ms,可以看到,混合插入排序的算法相比原来的算法有一定的提升,所以有些时候,分析数据以及数据输入特征,以及输入的内容是有必要的,分析可以让自己设计出更为高效的代码完成工作。

猜你喜欢

转载自blog.csdn.net/qq_18870127/article/details/84339783