记一次归并排序调优

归并排序核心思想:

合并两个有序数组为一个有序数组是很轻松的。

步骤:
  1. 将一个数组不断的从中间分成两个数组
  2. 对分后的两个数组不断重复步骤一,直到数组的长度为1
  3. 对分成的两个数组进行合并,此时就是分成的两个数组都已经是有序的

这段时间复习了一下数据结构的知识,对归并排序进行了重新的编写,采用的java语言,但通过测试20万个随机数据发现,归并排序的效率和选择排序的效率竟然差不多,对此我陷入了深深的怀疑,究竟是哪个地方出错了呢?
在这里插入图片描述
在此,先贴出原代码:

public int[] mergeSort(int[] array) {

        //采取先分后治的思想
        if(array.length > 1){

            //分
            int[] left = mergeSort(leftArray(array));
            int[] right = mergeSort(rightArray(array));
            //合
            array = mergeArray(left,right);
        }
        return array;
    }

    //获取数组的左半边
    private int[] leftArray(int[] a){
        int[] b = new int[(a.length + 1)/2];
        for (int i = 0; i < b.length; i++) {
            b[i] = a[i];
        }
        return b;
    }

    //获取数组的右半边
    private int[] rightArray(int[] a){
        int[] b = new int[a.length/2];
        int lenA = (a.length + 1) / 2;
        for (int i = 0; i < b.length; i++) {
            b[i] = a[i + lenA];
        }
        return b;
    }

    //合并两有序数组
    private int[] mergeArray(int[] left, int[] right){
        int[] a = new int[left.length + right.length];
        int indexL = 0;
        int indexR = 0;
        int lenL = left.length;
        int lenR = right.length;
        for (int i = 0; i < a.length; i++) {
            a[i] = left[indexL]  < right[indexR] ? left[indexL++] : right[indexR++];
            if(indexL == lenL || indexR == lenR){
                //满足条件跳出循环
                break;
            }
        }
        if(indexL == lenL){
            //将右侧数组依次装进合并数组内
            for (int i = indexL + indexR; i < a.length; i++) {
                a[i] = right[indexR++];
            }
        }else if(indexL == indexR){
            //将左侧数组依次装进合并数组内
            for (int i = indexL + indexR; i < a.length; i++) {
                a[i] = left[indexL++];
            }
        }
        return a;
    }

经过思索可能造成速度缓慢的因素(打死我都不认为是归并的思想出了问题),在共产党的带领下,我揪出了那个问题的凶手,没错,凶手只有一个,那就是不断的申请新数组,开辟空间是很耗时的,难怪效率那么慢了,原来大部分的时间都用来开矿了!惭愧,惭愧,这就是当代程序员滥用空间的血淋淋的例子,怀念远古时期,前辈们用个变量都要思考着个变量是否必要使用,生怕造成一点空间的浪费,额,扯远了。。。

知道了问题的原因,那就要着手去解决这个问题,既然开辟空间耗时,那就不开辟空间了,尽量用原来的数组空间,顶多加个临时数组,那这样问题能得到解决吗?说实话,我现在也不知道运行起来效率能提升多少,但提升是肯定的,期待中…

修改后的代码,如下:

public int[] mergeSort(int[] array) {
        if(array == null || array.length == 0){
            return null;
        }
        //该temp为了减少doMerge中的创建临时数组造成的性能下降而提前设置的
        int[] temp = new int[array.length];
        merge(array, 0, array.length - 1, temp);
        return array;
    }

    private void merge(int[] array, int start, int end, int[] temp){
        if(start < end){
            //分
            int mid = (start + end) >> 1;
            merge(array, start, mid, temp);
            merge(array, mid + 1, end, temp);
            //治
            doMerge(array,start,mid,end, temp);
        }
    }

    private void doMerge(int[] array, int start, int mid, int end, int[] temp){
        int left = start;
        int right = mid + 1;
        int index = start;
        while(left <= mid && right <= end){
            temp[index++] = array[left] > array[right] ? array[left++] : array[right++];
        }

        //将剩余一边的数组全部依次导入
        while(left <= mid){
            temp[index++] = array[left++];
        }

        while (right <= end){
            temp[index++] = array[right++];
        }

        //将临时数组导入array
        for (int i = start; i <= end; i++) {
            array[i] = temp[i];
        }
    }

话不多说,有什么问题跑起来再说,当当当,结果如下:
在这里插入图片描述
OMG!怎么会这样,啊啊啊啊啊啊啊啊!

我一定是看错了,不行我要去看眼科,什么情况啊,我改动了那么大,怎么会一点变化都没有,而且跟选择排序时间基本一样。

我好像,发现了什么,跟选择排序的时间基本一样=.=

呵呵,归并排序调用了选择排序
在这里插入图片描述
辣眼睛,=.=

改回来吧,看看改后的归并排序效率有多高
在这里插入图片描述

再对比下,改之前的归并排序时间
在这里插入图片描述
唔,证明我的改动还是相当赞的,20完个数据快了80%,现在加大数据量,看看效果如何。

先看老版的一亿个随机数的排序时间:
在这里插入图片描述

在看新版的一亿个随机数的排序时间
在这里插入图片描述

唔,大体这样了,超大数据的时候快了25%吧,还是没达到我的预期,我觉得起码要快两倍呢,桑心

最后给一点建议,如果有同学想理解归并排序的思想,可以看老版代码,如果想更快的速度,可以看新版代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值