高级排序:归并排序

一.概述
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

二.原理
1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止。 (使用递归来分组)
2.将相邻的两个子组进行合并成一个有序的大组,在归并的过程中进行排序。
3.不断的重复步骤2,直到最终只有一个组为止。

总示意图:
在这里插入图片描述

下面以一个例子展示归并的过程:
首先假设在归并的过程中,有以下左右两个已排好序的子组,为了将两个子组合并在一起并且在合并的过程中要排好序,这里需要一个辅助数组assist。

在此建立三个指针,分别是辅助数组上的指针i ,左子组的指针p1和右子组的指针p2。
将左子组和右子组指针上的数进行比较,小的数便放到辅助数组中,放完后该子组和辅助子组的指针++。
在这里插入图片描述如上图,左子组指针都指向索引0处数字为5,右子组指针指向索引0处数字为2,2<5,所以将右子组的2放到辅助数组第一个位置,然后右子组和辅助数组的指针后移一位即++。在这里插入图片描述
由上图,右子组指针此时来到索引1的位置,继续将左子组的5和右子组的8比较,5<8,将5放入辅助数组,而后依然是辅助数组的指针i和左子组p1分别++。
在这里插入图片描述
由上图,同理将左子组的6和右子组的8进行比较,6<8,将6放到辅助数组,p1和i分别后移一位。
在这里插入图片描述

当p1将7放到索引i处后,左子组的索引p1到了最后一位。而此时左子组已经全部放到了辅助数组,说明右子组剩下的数字比左子组最大的数字都要大。所以将右自组剩下的数依次都放到辅助数组就能满足左小右大的顺序。
在这里插入图片描述
7<8,将7放到辅助数组,指针后移。
在这里插入图片描述
由于左子组已经完事了,将右子组剩下的依次放入辅助数组即可。
在这里插入图片描述
最后将辅助数组拷贝回原数组即可。

三.Java代码实现

public class merge {
    public static  int[]assist;
    public static void sort(int[]a) { //入口
        assist=new int[a.length]  
        int lo = 0;
        int hi = a.length - 1;
        sort(a, lo, hi);}

	// 递归用于分组
    private static void sort(int[] a, int lo, int hi) { // 分 !
        if (lo >= hi) {// base case
            return;
        }
        int mid=lo+(hi-lo)/2;
        sort(a,lo,mid);  // 区别快排 !
        sort(a,mid+1,hi);
        // 后序遍历
        merge(a,lo,mid,hi); 
    }

    // 合并两个数组
    public static void merge(int[]a,int lo,int mid,int hi){ //治 !
        //定义三个指针
        int i=lo;
        int p1=lo;
        int p2=mid+1;//右子组的第一个索引是mid+1
        while((p1<=mid)&&(p2<=hi)){ //左右自组任意指针越界即退出循环
            if(a[p1]<=a[p2]){
                assist[i++]=a[p1++]; //先赋值,再自增!
            }else{
                assist[i++]=a[p2++];
            }
        }
        // 若有一边到头,到头的指针最后会++,超过范围跳出while
        // 以下两个while只会进入一个
        while(p1<=mid){ //注意此处有等号
            assist[i++]=a[p1++];
        }
        while(p2<=hi){
            assist[i++]=a[p2++];
        }

        //将排好序的辅助数组拷贝到原数组
        for(int j=lo;j<=hi;j++){ //此处index<=hi ,而不是a.length
            a[j]=assist[j];
        }
    }

    public static void main(String[] args) {
        int[]a={8, 4, 5, 7, 1, 3, 6, 2};
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

运行结果:

[1, 2, 3, 4, 5, 6, 7, 8]

需要注意的地方:

  1. 这里使用了sort方法的重载,sort()只是为了输入的时候更方便一点,可以在测试时自己加入lo和hi参数而不是用sort()方法。
  2. 排序的过程在merge方法中,每分完一次都会进行归并。
  3. 递归一定要有终止条件,否则栈溢出,这里的终止条件是lo>=hi
  4. 分组时 ,int mid=lo+(hi-lo)/2; 和 int mid=(lo+hi)/2 看似效果一样,但当数据较大时后者可能会更容易超出int的数据范围。
  5. 由于sort方法使用了递归,且merge方法在递归的sort方法内部,所以在归并左右两个子组时,各子组自身已经完成了排序!

在这里插入图片描述

此处使用<=是为了排序的稳定性,否则遇到相同的元素时,会把p2的传到辅助数组,则相当于将p1和p2相等的元素交换了位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值