归并排序算法

给定你一个长度为 n的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n。

第二行包含 n个整数(所有整数均在 1∼1091∼109 范围内),表示整个数列。

输出格式

输出共一行,包含 n个整数,表示排好序的数列。

数据范围

1≤n≤100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5

代码如下:

package com.zy.Acwing.mergeSort;
import java.util.Scanner;
//本段代码主要实现了归并排序算法
public class P787 {
	static int N = 100010;
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		int[] arr = new int[N];
		for(int i = 0 ; i < n ; i++) {
			arr[i] = scan.nextInt();
		}
		merge_sort(arr,0,n-1);
		for(int i = 0 ; i < n ; i++) {
			System.out.print(arr[i]+" ");
		}
	}
	public static void merge_sort(int[] arr,int left,int right) {
		if(left>=right)  return;
		int mid = left + right >> 1;
		merge_sort(arr,left,mid);
		merge_sort(arr,mid + 1,right);
		int k = 0,i = left,j = mid+1;
		int[] temp = new int[right-left+1];
		while(i <= mid && j <= right) {
			if(arr[i] <= arr[j])   temp[k++] = arr[i++];
			else temp[k++] = arr[j++];
		}
		while(i <= mid) temp[k++] = arr[i++];
		while(j <=right) temp[k++] = arr[j++];
		for(i = left,k = 0;i <= right;i++,k++) arr[i] = temp[k];
	}
	

}

自己基于目前的了解对此段代码进行一些解释:

  • if(left>=right)  return; 递归结束的条件,当数组中只有一个数或者达到left>=right条件时,直接结束递归;
  • int mid = left + right >> 1; 取整个数组的分隔线,向上取整,使整个数组一分为二;
  • merge_sort(arr,left,mid); merge_sort(arr,mid + 1,right); 

      使用递归思想,使用mid作为分隔线,划分为arr[left,....mid]和arr[mid+1,....right]两个数组;

       递归之后的两个数组都是有序的,下面的代码就是在两个有序数组中,归并为一个大的有序数组;

  • int k = 0,i = left,j = mid+1; int[] temp = new int[right-left+1];      

        定义一个temp数组,用来存放排序之后的元素,

        i为一个数组的左边界,j为另一段数组的左边界(前面已经提到一个数组被分割为两个数组)

while(i <= mid && j <= right) {
	if(arr[i] <= arr[j])   temp[k++] = arr[i++];
	else temp[k++] = arr[j++];
}

         本段代码首两个数组的元素都没有比较完,如果其中一个已将全部比较完成,另一组没有,则本循环结束,较小的放进temp,同时下标向右移,较大的留下继续比较;

while(i <= mid) temp[k++] = arr[i++];
while(j <=right) temp[k++] = arr[j++];

        本段代码实现的功能是如果其中一个数组已将全部比较完成,另一组没有还剩下的元素,把剩下的元素((剩下的元素都是有序的,无须再次比较)放进temp中;

  • for(i = left,k = 0;i <= right;i++,k++) arr[i] = temp[k];

        将temp中排好序的元素放到arr中;

  • 为什么不用 mid - 1 作为分隔线呢?

        即 merge_sort(q, l, mid - 1 ), merge_sort(q, mid, r)

        因为 mid = l + r >> 1 是向下取整,mid 有可能取到 l (数组只有两个数时),造成无限划分

        解决办法: mid 向上取整就可以了, 即 mid = l + r + 1 >> 1;

  • 为什么进行递归之后,两段数组变为有序的呢?        

        首先要明白归并算法的整个流程是什么的,B站中有一个动画展示的比较清楚,地址如下:归并算法动画演示icon-default.png?t=N7T8https://www.bilibili.com/video/BV1Je4y1K7Qd/?spm_id_from=333.999.top_right_bar_window_custom_collection.content.click&vd_source=f83925eb71a2ec1683580bbf22db27ec        通过递归的不断执行,当划分为n个数组时,一个数组只有一个元素,一个数本身就是有序的,满足l==j的条件,无需执行后面的代码,结束最后一次的递归代码的执行;接着实现倒数第二次的递归,此时的数组元素是由两个元素组成,通过后面代码的比较,将这个含有两个元素的数组进行排序,完成之后,接着实现倒数第三次的递归,此时的数组元素是由四个元素组成,将这个含有两个元素的数组进行排序......,知道第一次递归结束,此时一个数组中的n个元素是有序的,排序完成;

其实,整个递归执行过程是一棵二叉树,先会递归到整棵树的底部,底部只有一个节点一定有序,然后在回溯时不断合并相邻两个区间,每次合并之后都会将合并之后的区间排好序。那么当合并到根节点时,整个区间就有序了。

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值