505、java-归并排序

归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。

归并排序的实现分为递归实现与非递归(迭代)实现:
  1.递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。
  2.非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

合并区间
  归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个有序序列的操作,归并操作步骤如下:

1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动该指针到下一位置
4.重复步骤3直到某一指针到达序列尾
5.将另一序列剩下的所有元素直接复制到合并序列尾

递归代码:

    /**
     * 归并排序
     * @param arr     待排序数据
     * @param left    开始坐标
     * @param right   结束坐标
     */
    public static void mergeSortIteration(int arr[],int left ,int right){
        //1.当左右坐标相等,则可以判断当前待比较数组只有一个元素,则不需要继续拆分
        if(arr == null || arr.length == 0 || left == right)
            return;
        //2.取数组中间坐标分界线,这里取坐标相加除2,例如左右坐标范围是3与10取其13/2=6为中间坐标,不是相减操作
        int min = (left+right)/2;
        //3.比较数组一分为2
        mergeSortIteration(arr,left,min);
        mergeSortIteration(arr,min+1,right);
        //比较合并数组--完成排序
        merge(arr,left,min,right);
    }
    public static void merge(int arr[],int left,int min,int right){
        //1.获取零时数组长度,用于创建零时数组
        int[] temp = new int[right-left+1];
        //2.获取待比较数据开始坐标,零时数组开始坐标变量
        int i = left;
        int j = min + 1;
        int index = 0;
        //3.比较合并数据,上面所说的重复操作就是这一步
        while(i <= min && j<= right){
            temp[index++] = arr[i] <= arr[j]?arr[i++]:arr[j++];
        }
        //4.未合并数据入队
        while(i<=min){
            temp[index++] = arr[i++];
        }
        while(j<=right)
            temp[index++] = arr[j++];
        //5.零时数组中的排序数据进行归位
        for (int k = 0;k<temp.length;k++){
            arr[left++] = temp[k];
        }
    }

非递归实现:

假设数据:a={0,1,2,3,4,5,6,7},i=1,i<a.length ,i*=2;

当i=1时:
{0}-{1} {2}-{3} {4}-{5} {6}-{7},随着内部循环调用分为这样的合并比较数据
当i=2时:
{0,1}-{23} {4,5}-{7,7} ,两两之间比较合并。
当i=4时:
{0,1,2,3}-{4,5,7,7} ,两两之间比较合并。

看如下代码,其外层循环用于区间划分,内层循环用于相邻两个区间的数据排序合并操作,注意合并操作的思路

	public static void mergeSortIteration(int[] arr){
		if(arr == null || arr.length == 0)
			return;
		int left,mid,right;
		int len = arr.length;
		//其主要在 i*=2 来划分区间
		for (int i = 1; i < len; i*=2) {
			left = 0;
			while(left + i < len){
				
//建设i=2时:mid=left+i=2, right=mid+i=4;可知left-mid-right-->0-2-4,就可以发现其0-4其实有5个元素没法进行比较,所以需要减去一个,4个元素就可以两两之间合并,那么怎么减去这一个元素呢,由右边界开始减一,right=right-1=3,不要忘记mid减1,因为mid其实就是left这个区间的右边界线,所以mid=mid-1=1,得出间隔left=0,mid=1,right=3那么元素就是{0,1}{2,3},这就是如下操作的解释。
				mid = left + i -1;
				//注意mid+i时不要超出数据的长度,其最大的右边界是len-1;
				right = (mid+i < len?mid+i:len -1); 
				merge(arr,left,mid,right);
				left = right+1; //开始下一个区间的左边界
			}
		}
		
	}

在这里插入图片描述

归并排序除了可以对数组进行排序,还可以高效的求出数组小和(即单调和)以及数组中的逆序对,详见 https://www.jianshu.com/p/3ab5033074f1。

附注:如果了解线段树这种数据结构,会更好的理解递归调用的方式

来源:http://www.cnblogs.com/eniac12/p/5329396.html#s32

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值