归并排序算法

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤100000

输入样例:

复制代码
5
3 1 2 4 5

输出样例:

复制代码
1 2 3 4 5

代码如下:

java 复制代码
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为另一段数组的左边界(前面已经提到一个数组被分割为两个数组)

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

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

java 复制代码
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站中有一个动画展示的比较清楚,地址如下:归并算法动画演示https://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个元素是有序的,排序完成;

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

相关推荐
Hello.Reader1 小时前
算法基础(十)——分治思想把大问题拆成小问题
java·开发语言·算法
绛橘色的日落(。・∀・)ノ2 小时前
机器学习之评估与偏差方差分析
算法
消失的旧时光-19432 小时前
C语言对象模型系列(四)《Linux 内核里的 container_of 到底是什么黑魔法?》—— 一篇讲透 Linux 内核的“对象模型”核心技巧
linux·c语言·算法
AI_Ming3 小时前
从0开始学AI:层归一化,原来是这回事!
算法·ai编程
WL_Aurora3 小时前
备战蓝桥杯国赛【Day 8】
算法·蓝桥杯
智者知已应修善业3 小时前
【51单片机模拟生日蜡烛】2023-10-10
c++·经验分享·笔记·算法·51单片机
MediaTea3 小时前
Scikit-learn:从数据到结构——无监督学习的最小闭环
人工智能·学习·算法·机器学习·scikit-learn
智者知已应修善业3 小时前
【51单片机如何让LED灯从一亮到八,再从八亮到一】2023-10-13
c++·经验分享·笔记·算法·51单片机
qeen873 小时前
【数据结构】二叉树相关经典函数C语言实现
c语言·数据结构·c++·笔记·学习·算法·二叉树
良木生香4 小时前
【C++初阶】STL——List从入门到应用完全指南(1)
开发语言·数据结构·c++·程序人生·算法·蓝桥杯·学习方法