归并排序:就是递归加合并
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("结束了");
}
}
结果如下:
编写文档的参考视频:马士兵十大排序