归并排序就是递归加合并

归并排序:就是递归加合并

1.递归思路

给你一组数据: 8, 6 , 5, 7,3,9,5,7

从中间划分,先让左边的数排好,再让右边的数排好,最后用merge()合并排序。

我们先把合并这个merge()功能当成黑盒,我们已经实现了就可以用了。

首先左边的数是 8 , 6, 5 , 7 排好序 右边的数是 3,9,5,7 排好序,

最后merge()就完成了。

让左边的数: 8 , 6, 5 , 7 排好序 也这个思想:从中间划分,先让左边 8,6 排好序, 再让 5, 7 排好序,最后merge(), 左边的 8 , 6 也是这个思想,剩下一个的时候,merge()一下就成了 6, 8, 右边的merge()一下成了5, 7, 整体merge()一下就成了 5, 6,7, 8

右边也是类似情况,右边成了 3, 5, 7, 9

所以递归的代码就有了

	/**
	 * 归并排序
	 * @param arr   数组名
	 * @param left  最左边的位置
	 * @param right  最右边的位置
	 */
	private static void MergeSort(int[] arr,int left,int right) {//实现arr数组,left到right上的有序
		if(arr  == null || arr.length < 2 ) {//边界
			return;
		}
		if(left  ==  right)return;//如果剩下自己就可以直接返回了,肯定是有序了
		int mid = left + (right - left)/2; //从中间位置划分
		MergeSort(arr,left,mid);     //[left , mid]  ,让左边的区域排好序
		MergeSort(arr,mid + 1,right);//[mid + 1 ,right],让右边的区域排好序
		merge(arr,left,mid+1,right); //最后把两个合并一下
		
	}
注意:写递归一定要有返回值,可以根据事情的完成情况去返回,如果已经完全完成了,那就直接返回,就像此递归中的,我们要完成的事情是实现arr数组,left到right上的有序;当left == right 的时候,说明只有一个数,一个数肯定是有序的。

2.白盒merge()合并函数实现

给一组merge()的数组: 7, 8,9, 10, 3, 4, 5, 6

首先中间位置是下标 3 ,左边的数组 7 , 8,9,10 已经有序了,右边的数组 3,4,5,6 也已经有序了,接下来合并了:

​ left: 7,8,9,10 right: 3,4 ,5,6

新建一个temp数组大小是两边数组之合: temp: 0 ,0,0,0, 0,0,0,0

int L = 0,代表着left数组的下标 int R = 0,代表着right数组的下标,int T = 0,代表着temp数组的下标。

首先判断left[L] 与 right[R] ,小于的就赋值给temp[T],然后temp的下边加1,较小值所在数组下标加1,以此类推。

一开始, L = 0, R = 0, T = 0;因为 right[0] < left[0],所以temp = right[0], R + 1,T + 1 temp: 3, 0,0,0, 0,0,0,0

L = 0, R = 1, T = 1;因为 right[1] < left[0],所以temp[1] = right[1], R + 1,T + 1 temp: 3, 4,0,0, 0,0,0,0

L = 0, R = 2, T = 2;因为 right[2] < left[0],所以temp[2] = right[2], R + 1,T + 1 temp: 3, 4,5,0, 0,0,0,0

L = 0, R = 3, T = 3;因为 right[3] < left[0],所以temp [3]= right[3], R + 1,T + 1 temp: 3, 4,5,6, 0,0,0,0

当有一个数组来到的最后面,就要看看要不要处理另一个数组,处理另一个不需要比较,直接赋值给temp就可以了

L = 0, T = 4 ; temp[4] = left[0] ;R + 1,T + 1 temp: 3, 4,5,6, 7,0,0,0

L = 1, T = 5 ; temp[5] = left[1] ;R + 1,T + 1 temp: 3, 4,5,6, 7,8,0,0

L = 2, T = 6 ; temp[6] = left[2] ;R + 1,T + 1 temp: 3, 4,5,6, 7,8,9,0

L = 3, T = 7 ; temp[7] = left[3] ;R + 1,T + 1 temp: 3, 4,5,6, 7,8,9,10

排序完成;

merge()代码如下:

	/**
	 * merge合并两个有序数组
	 * @param arr  //目标数组
	 * @param left   //指定目标数组的左边界
	 * @param mid   //目标数组的中间边界
	 * @param right//目标数组的最右边界
	 */
	private static void merge(int[] arr,int left,int mid,int right) {
		int i = left;   //左边数组起始下标
		int j = mid;   //中间位置坐标
		int k = 0;  //temp数组的下标,起始为0
		int[] temp = new int[right - left + 1];//声明temp来保存合并后的数
		while(i < mid && j <= right) {		//i是左边数组下标,左边的数组到达mid - 1说明是最后一个位置;j同理。
        temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; 
        //首先判断 arr[i]  与  rarr[j] ,小于的就赋值给temp[k] ,然后temp的下边加1,较小值所在数组下标加1
			/*if(arr[i] <=  arr[j]) {
				temp[k] = arr[i];
				k++;
				i++;
			}else {
				temp[k] = arr[j];
				k++;
				j++;
			}*/
		}
		while(i < mid)  temp[k++] = arr[i++]; //如果右边的数组先到达边界,左边的数组还有值,那就会执行左边数组的赋值给temp数组操作
		while(j <= right)  temp[k++] = arr[j++];// //如果左边的数组先到达边界,右边的数组还有值,那就会执行右边数组的赋值给temp数组操作
		for(int m = 0; m < temp.length; m++) arr[left+m] = temp[m];//将合并后的结果在arr目标数组中修改好。
		
	}

3.编写测试代码

代码如下:

public class MergeSort{

	
	

	/**
	 * 打印数组
	 * @param arr
	 */
	public static void  printArr(int[] arr) {
		for(int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
		System.out.println();
	}
	/**
	 * 交换两个数
	 * @param arr 
	 * @param i
	 * @param j
	 */
	private static void swap(int[] arr, int i, int j) {
		
		
	}
	/**
	 * 归并排序
	 * @param arr   数组名
	 * @param left  最左边的位置
	 * @param right  最右边的位置
	 */
	private static void MergeSort(int[] arr,int left,int right) {//实现arr数组,left到right上的有序
		if(arr  == null || arr.length < 2 ) {//边界
			return;
		}
		if(left  ==  right)return;//如果剩下自己就可以直接返回了,肯定是有序了
		int mid = left + (right - left)/2; //从中间位置划分
		MergeSort(arr,left,mid);     //[left , mid]  ,左边的区域排好序
		MergeSort(arr,mid + 1,right);//[mid + 1 ,right], 右边的区域排好序
		merge(arr,left,mid+1,right); //最后把两个合并一下
		
	}
	/**
	 * 归并排序merge1
	 * @param arr
	 */
	/*private static void merge(int[] arr) {
		int mid = arr.length / 2;
		int i = 0;
		int j = mid + 1;
		int k = 0;
		int[] temp = new int[arr.length];
		while(i <= mid && j < arr.length) {
			
        temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
			
			/*if(arr[i] <=  arr[j]) {
				temp[k] = arr[i];
				k++;
				i++;
			}else {
				temp[k] = arr[j];
				k++;
				j++;
			}
		}
		while(i <= mid)  temp[k++] = arr[i++];
		while(j < arr.length)  temp[k++] = arr[j++];
		printArr(temp); 
		
	}*/
	
	
	/**
	 * merge合并两个有序数组
	 * @param arr  //目标数组
	 * @param left   //指定目标数组的左边界
	 * @param mid   //目标数组的中间边界
	 * @param right//目标数组的最右边界
	 */
	private static void merge(int[] arr,int left,int mid,int right) {
		int i = left;   //左边数组起始下标
		int j = mid;   //中间位置坐标
		int k = 0;  //temp数组的下标,起始为0
		int[] temp = new int[right - left + 1];//声明temp来保存合并后的数
		while(i < mid && j <= right) {		//i是左边数组下标,左边的数组到达mid - 1说明是最后一个位置;j同理。
        temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; 
        //首先判断 arr[i]  与  rarr[j] ,小于的就赋值给temp[k] ,然后temp的下边加1,较小值所在数组下标加1
			/*if(arr[i] <=  arr[j]) {
				temp[k] = arr[i];
				k++;
				i++;
			}else {
				temp[k] = arr[j];
				k++;
				j++;
			}*/
		}
		while(i < mid)  temp[k++] = arr[i++]; //如果右边的数组先到达边界,左边的数组还有值,那就会执行左边数组的赋值给temp数组操作
		while(j <= right)  temp[k++] = arr[j++];// //如果左边的数组先到达边界,右边的数组还有值,那就会执行右边数组的赋值给temp数组操作
		for(int m = 0; m < temp.length; m++) arr[left+m] = temp[m];//将合并后的结果在arr目标数组中修改好。
		
	}
	 /**
	  * 判断是否有序
	  * @param arr
	  * @return
	  */
	 public static boolean isSorted(int [] arr) {
		 if(arr.length < 2) {
			 return true;
		 }
		int max = arr[0];
		 
	   for(int i = 1; i  < arr.length;i++) {
		   if(max > arr[i]) {
			   return false;
		   }
		   max = Math.max(max, arr[i]);
	   }
		 return true;
	 }
	 /**
	  * 备份数组
	  * @param arr
	  * @return
	  */
	 public static int[] copyArray(int [] arr) {
		 int [] ans = new int[arr.length];
		 for(int i = 0; i < arr.length; i++) {
			 ans[i] = arr[i];
		 }
		 return ans;
	 }
	 public static int[] lenRandomValueRandom(int maxLen, int maxValue) {
		 int len = (int)(Math.random() * maxLen);
		 int[] ans = new int[len];
		 for(int i = 0; i < len; i++) {
			 ans[i] = (int)(Math.random() * maxValue);
		 }
		 return ans;
	 }
	

	public static void main(String[] args) {
		int maxLen = 50;
		int maxValue = 1000;
		int testTime = 10000; //设置测试次数
		for(int i = 0; i  < testTime; i++) {
			int[] arr1 = lenRandomValueRandom(maxLen, maxValue);//产生随机数组
			int[] temp = copyArray(arr1);  //留下数组备份,方便找出初始测试用例,因为排序后顺序乱了
			MergeSort(arr1,0,arr1.length - 1);   //测试归并排序
			if(!isSorted(arr1)) {//isSorted()为测试是否有序函数
				for(int j = 0; j  < temp.length;j++) {
					System.out.print(temp[j]+" ");
				}                         //打印备份数组
				System.out.println();
				System.out.println("归并排序错了");
				return;
			}
			
		}
		System.out.println("结束了");
		
	}
	

}

结果如下:

在这里插入图片描述

编写文档的参考视频:马士兵十大排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值