一、归并
归并排序先懂归并那接下来的代码也就更加容易理解了。
假设有一个数组那我们如何进行归并呢?如下数组两边有序的数组才能进行归并
arr= {1, 4, 7, 9, 2, 5, 8}
我们将这个数组看成两个小有序数组,但是不是分开
arrLeft = {1, 4, 7, 9} arrRight = {2, 5, 8}
那么我们将这两个数合并成一个有序数组
①先创建一个新数组和arr的长度一致,前期准备
//构建一个新数组 和原来的数组长度一致
int[] newArr = new int[arr.length];
//将数组分为 左右两个
int mid = arr.length / 2;
// 左边数组下标
int i = 0;
//右边数组下标
int j = mid + 1;
//newArr 数组下标
int k = 0;
②比较 arrLeft[i]和arrRight[j]的大小,arrLeft[i]小 则将arrLeft[i]加入新数组newArr[k]中,此时arrLeft和newArr的下标应该要指向下一个位置。arrRight[j]小 则将arrRight[j]加入新数组newArr[K]中,此时 arrRight和newArr的下标指向下一个位置.
③然后重复步骤②
while (i <= mid && j < arr.length) {
if (arr[i] <= arr[j]) {
newArr[k] = arr[i];
k++;
i++;
}else{
newArr[k] = arr[j];
k++;
j++;
}
}
④此时我们发现arrRight先终止了循环,但是arrLeft的值还没有完全被加入到新数组中,所以我们应该在做一步操作
while (i <= mid) newArr[k++] = arr[i++];
while (j < arr.length) newArr[k++] = arr[j++];
这样就得到了新数组且有序 完整代码如下
public static void merge() {
int[] arr = {1, 4, 7, 9, 2, 5, 8};
//构建一个新数组 和原来的数组长度一致
int[] newArr = new int[arr.length];
//将数组分为 左右两个
int mid = arr.length / 2;
// arrLeft数组下标
int i = 0;
// arrRight数组下标
int j = mid + 1;
//newArr 数组下标
int k = 0;
while (i <= mid && j < arr.length) {
//arrLeft[i] 小于等于 arrRight[j]
if (arr[i] <= arr[j]) {
//将arrLeft[i]的值加入 新数组newArr[k] 并且 k++ i++
newArr[k] = arr[i];
k++;
i++;
}else{
//将arrRight[j]的值加入 新数组newArr[k] 并且 k++ j++
newArr[k] = arr[j];
k++;
j++;
}
}
//如果某一边没有被全部加入新数组 则将其全部加入新数组
while (i <= mid) newArr[k++] = arr[i++];
while (j < arr.length) newArr[k++] = arr[j++];
//打印新数组
for (int a: newArr) {
System.out.print(a + " ");
}
}
接下来将代码进行改造,改成适合所有归并的代码
/**
*
* @param arr 传入数组
* @param leftPtr 左边数组下标
* @param rightPtr 右边数组下标 and 左边数组边界 = rightPrt-1
* @param rightBound 右边数组边界
*/
public static void merge(int[] arr, int leftPtr, int rightPtr, int rightBound) {
//构建新数组
int[] newArr = new int[rightBound - leftPtr +1];
int mid = rightPtr -1;
// arrLeft数组下标
int i = leftPtr;
// arrRight数组下标
int j = rightPtr;
//newArr 数组下标
int k = 0;
while (i <= mid && j <= rightBound) {
newArr[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
//如果某一边没有被全部加入新数组 则将其全部加入新数组
while (i <= mid) newArr[k++] = arr[i++];
while (j <= rightBound) newArr[k++] = arr[j++];
//将有序数组赋给以前的数组
for(int m = 0; m < newArr.length; m++){
arr[leftPtr + m] = newArr[m];
}
}
改造完大家肯定有点懵咋突然成这样了,接下来看一张图
可以发现归并是用一次一次分解 分解到最小值 一个数组中仅仅只有一个值了之后,那么这个数组肯定是有序的,然后在一次一次合并,最后得到了有序而完整的数组。
刚刚讲了合 那么现在来讲 如何分。
很明显从中间分。
由分了之后再分,分了再分 这不就是递归嘛。所以直接上代码.
package sort;
/** 归并排序(小 ——> 大)
* @author codelmh
* @data 2021/11/20
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {9, 2, 4, 3, 8, 5, 6};
sort(arr,0, arr.length-1);
for (int i =0; i < arr.length; i++){
System.out.print(arr[i] + " ");
}
}
public static void sort(int[] arr, int left, int right){
if (left == right) return;
//从中间分开 (left + right)/2 如果数值大将会超出int范围所以使用
int mid = left + (right-left) / 2;
//左边
sort(arr, left, mid);
//右边
sort(arr, mid+1, right);
//最后调用合
merge(arr, left, mid+1, right);
}
/**
*
* @param arr 传入数组
* @param leftPtr 左边数组下标
* @param rightPtr 右边数组下标 and 左边数组边界 = rightPrt-1
* @param rightBound 右边数组边界
*/
public static void merge(int[] arr, int leftPtr, int rightPtr, int rightBound) {
//构建新数组
int[] newArr = new int[rightBound - leftPtr +1];
int mid = rightPtr -1;
// arrLeft数组下标
int i = leftPtr;
// arrRight数组下标
int j = rightPtr;
//newArr 数组下标
int k = 0;
while (i <= mid && j <= rightBound) {
newArr[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
//如果某一边没有被全部加入新数组 则将其全部加入新数组
while (i <= mid) newArr[k++] = arr[i++];
while (j <= rightBound) newArr[k++] = arr[j++];
//将有序数组赋给以前的数组
for(int m = 0; m < newArr.length; m++){
arr[leftPtr + m] = newArr[m];
}
}
}