归并排序(内部)总结

归并排序(内部)总结

归并排序建立在归并操作(即将两个有序数组归并为一个更大的有序数组)上的一种有效的排序算法,该算法是分治法的一个非常典型的应用

我们常用的自顶向下归并排序分为两步:
I. 无序数组逐层递归分解为子数组->mergeSort()
II. 有序子数组逐层归并为有序数组->merge()

1.借助一个辅助数组的归并排序

一般的归并操作需要用到三个数组,以下这种方法虽然只用了两个数组,但也只是少了一次数组声明而已。


public class MergeSort {

    private static int[] temp;//归并所需的辅助数组

    //辅助数组也可以在方法的参数里声明
    //原地归并的抽象化(升序)
    public static void merge(int[] array, int lo, int mid, int hi){
        int i = lo, j = mid + 1; //i,j分别代表左半边和右半边的开始
        for(int k = lo; k <= hi; k++) {
            temp[k] = array[k];//将array[lo...hi]复制到将temp[lo...hi]
        }
        for(int k = lo; k <= hi; k++) {//归并回到array[lo...hi]
            //左半边用尽,取右半边元素
            if     (i > mid)            array[k] = temp[j++]; 
            //右半边用尽取左半边元素
            else if(j > hi)             array[k] = temp[i++];
            //右半边的当前元素小于左半边的当前元素,取右半边元素
            else if(temp[j] < temp[i])  array[k] = temp[j++];
            //右半边的当前元素大于或等于左半边的当前元素,取左半边元素
            else                        array[k] = temp[i++];
        }
    }

    二分归并(递归实现)
    //1.自顶向下的归并排序基于原地归并
    public static void mergeSort(int[] array) { 
        temp = new int[array.length];//一次性分配空间
        mergeSort(array, 0, array.length - 1);
    }
    public static void mergeSort(int[] array, int lo, int hi){
        if(hi < lo) return;
        int mid = lo + (hi - lo) / 2;//或(lo + hi) / 2
        mergeSort(array, lo, mid);
        mergeSort(array, mid + 1, hi);
        merge(array, lo, mid,  hi);
    }

自顶向下归并: 自顶向下归并
特点:

  • 是稳定的排序算法, 空间复杂度:O( n ), 时间复杂度:O(nlogn
  • 归并排序需要的比较次数是 12nlogn nlogn
  • 归并排序最多需要访问数组 6nlogn 次:每次归并最多访问数组 6n 次( 2n 次复制, 2n 次用来将排好序的元素移回,另外最多比较 2n 次)

归并排序的优化:
a.对小规模的数组可以使用插入排序减少递归的空间花费

b.可以在排序时,判断a[mid]是否小于a[mid+1],跳过merge()方法。因为子数组已经有序,所以a[mid]是前半部分的最大值,a[mid+1]是后半部分的最小值,就不用进行多余的归并操作

c.为了节省将元素复制到辅助数组作用的时间,可以在递归调用的每个层次交换原始数组与辅助数组的角色

另:没有任何的基于比较的算法保证使用少于 log(n!) ~ nlogn 次比较将长度为n的数组排序


2.自底向上的方法

一般我们习惯使用自顶向下的归并排序,但下面这种先逐步归并小数组的非递归方法也是可以的

    //2.自底向上的归并
    public static void mergeSortBU(int[] array) {
        //进行logn次两两归并
        int n = array.length;
        temp = new int[n];
        for(int sz = 1; sz < n; sz += sz) {//sz子数组大小
            for( int lo = 0; lo < n - sz; lo += sz + sz) {//lo子数组下标
                merge(array, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, n-1));
            }
        }
    }

}

自底向上归并: 自底向上归并

3.参考:《算法》(第四版)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值