排序算法—归并排序的Java实现,效率比选择排序要低?

初级Java开发人员面试,经常会碰到技术面试官问排序算法,几乎每次面试一遍都要刷一遍排序算法……,今天正在刷归并排序,居然发现在100000级别的数据量中归并算法的执行时间居然比选择排序要长,百思不得解,最后在比较多位大神blog中的代码实现,发现问题的根源,在此做一个记录,也算是一个成长道路上的积累吧。

归并排序算法的实现步骤(网上贴的)
 * 把长度为n的输入序列分成两个长度为n/2的子序列;
 * 对这两个子序列分别采用归并排序;
 * 将两个排序好的子序列合并成一个最终的排序序列。

选择排序算法实现:

    /**
     * 它的工作原理很容易理解: 1.初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;
     * 2.然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾; 3.以此类推,直到所有元素均排序完毕。
     *
     * @return int[]
     * @Author Administrator
     * @Date 2018年3月7日
     */
    public static void selectSort(int arr[]) {
        long start = System.currentTimeMillis();
        if (arr != null && arr.length > 1) {
            for (int i = 0; i < arr.length - 1; i++) {
                int min = i;
                for (int j = i + 1; j < arr.length; j++) {
                    if (arr[min] > arr[j]) {
                        min = j;
                    }
                }
                if (min != i) {
                    swap(arr, i, min);
                }
            }
        }
        System.out.println("select sort 耗时 : " + (System.currentTimeMillis() - start) + "ms");
    }

第一版归并算法实现:


    /**
     * 归并排序
     * 把长度为n的输入序列分成两个长度为n/2的子序列;
     * 对这两个子序列分别采用归并排序;
     * 将两个排序好的子序列合并成一个最终的排序序列。
     * @param arr
     */
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 1) {
            return;
        }
        long start = System.currentTimeMillis();
        doMergeSort(arr, 0, arr.length-1);
        System.out.println("merge sort 耗时 : " + (System.currentTimeMillis() - start) + "ms");
    }

    private static void doMergeSort(int[] arr, int start, int end) {
        if (start >=end) {
            return;
        }
        int mid = (start + end) >>1;

        doMergeSort(arr, start, mid);
        doMergeSort(arr, mid+1, end);
        merge(arr, start,mid, end);
    }

    /**
     *合并子序列
     * 
     * @param arr
     * @param start
     * @param end
     */
    private static void merge(int[] arr, int start,int mid, int end) {       *//注意此处,产生临时数据表,存储已经归并了的子序列
        *int[] temp = new int[arr.length];*
        int tp=start,lp=start,rp=mid +1;
        while(lp<=mid&&rp<=end){
            if(arr[lp]<=arr[rp]){
                temp[tp++] =arr[lp++] ;
            }else{
                temp[tp++] =arr[rp++] ;
            }
        }
        while(lp<=mid){
            temp[tp++] =arr[lp++] ;
        }
        while(rp<=end){
            temp[tp++] =arr[rp++] ;
        }
        int cp =start;
        //从临时数组拷贝到原数组
        while(cp<=end){
            arr[cp] = temp[cp];
            cp++;
        }
    }

运行结果比较:
数据量:10000
select sort 耗时 : 62ms
merge sort 耗时 : 112ms

归并排序耗时近乎选择排序耗时两倍。

改良后的代码实现:


    /**
     * 归并排序 把长度为n的输入序列分成两个长度为n/2的子序列;
     * 对这两个子序列分别采用归并排序;
     * 将两个排序好的子序列合并成一个最终的排序序列。
     * 
     * @param arr
     */
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 1) {
            return;
        }
        long start = System.currentTimeMillis();
        int[] temp = new int[arr.length];
        doMergeSort(arr, 0, arr.length - 1,temp);
        System.out.println("merge sort 耗时 : " + (System.currentTimeMillis() - start) + "ms");
    }

    private static void doMergeSort(int[] arr, int start, int end,int[] temp) {
        if (start >= end) {
            return;
        }
        int mid = (start + end) >> 1;

        doMergeSort(arr, start, mid,temp);
        doMergeSort(arr, mid + 1, end,temp);
        merge(arr, start, mid, end,temp);
    }

    /**
     * 合并子序列
     * 
     * @param arr
     * @param start
     * @param end
     */
    private static void merge(int[] arr, int start, int mid, int end,int[] temp) {

        // 此处new temp降低排序性能
        int tp = start, lp = start, rp = mid + 1;
        while (lp <= mid && rp <= end) {
            if (arr[lp] <= arr[rp]) {
                temp[tp++] = arr[lp++];
            } else {
                temp[tp++] = arr[rp++];
            }
        }
        while (lp <= mid) {
            temp[tp++] = arr[lp++];
        }
        while (rp <= end) {
            temp[tp++] = arr[rp++];
        }
        int cp = start;
        // 从临时数组拷贝到原数组
        while (cp <= end) {
            arr[cp] = temp[cp];
            cp++;
        }
    }

效率比较:
select sort 耗时 : 71ms
merge sort 耗时 : 6ms

效率得到飞一般的提升,那么两种实现的为何会相差这么多呢?
我的分析是在数据量大的时候,执行merge方法的次数必然增多,每调用merge方法,执行一次int[] temp = new int[arr.length];产生一个临时对象,降低了性能。改良后的实现,在外部调用merge时把临时数据表当做引用传入方法中,在整个排序过程中始终始终只需要一个额外的临时空间,因此效率得到大幅度上升。
网上有些Blog贴出的第一版的实现,再次我引以为鉴,踩过坑才能不入坑!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值