开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第二十二天,点击查看活动详情
六、归并排序
采用分治策略。分治法将问题分 (divide) 成一些小的问 题然后递归求解,而治(conquer) 的阶段则将分的阶段得到的各答案"修补"在一 起,即分而治之。
以上结构很像一棵完全二叉树,采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归 拆分子序列的过程
分析
上图合并了七次,下图为最后一次的合并详解:
import java.util.Arrays;
/**
* @author Kcs 2022/9/3
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
System.out.println("要排序的数组:" + Arrays.toString(arr));
//归并需要一个额外的空间
int[] temp = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, temp);
System.out.println("归并排序结果:" + Arrays.toString(arr));
}
/**
* 分解 + 合并 过程
*/
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
// 向左递归进行分解
mergeSort(arr, left, mid, temp);
//向右递归分解
mergeSort(arr, mid + 1, right, temp);
//合并
merge(arr, left, mid, right, temp);
}
}
/**
* 合并起来
* @param arr 原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
// 初始化i 左边有序序列的初始索引
int i = left;
// 初始化 j ,右边有序序列的初始索引
int j = mid + 1;
// 指向temp数组的当前索引
int t = 0;
// 1. 左右两边的数据,按照规则进行填充到 temp 数组,直到左右两边的有序序列,有一边完成为止
while (i <= mid && j <= right) {
//左边的有序序列的当前元素,小于等于右边有序序列的当前元素,即将左边的当前元素,拷贝到 temp 中
if (arr[i] < arr[j]) {
temp[t] = arr[i];
t += 1;
i += 1;
} else {
// 右边的比左边的元素大,填充到temp数据
temp[t] = arr[j];
t += 1;
j += 1;
}
}
// 2. 把剩余数据的一边的数据依次全部填充到 temp 中
while (i <= mid) {
//左边的 有序序列还有剩余元素,全部填充到 temp 中去
temp[t] = arr[i];
t += 1;
i += 1;
}
while (j <= right) {
//左边的 有序序列还有剩余元素,全部填充到 temp 中去
temp[t] = arr[j];
t += 1;
j += 1;
}
// 3. 将temp 数据的元素拷贝到 arr,并不是每次拷贝所有
t = 0;
int tempLeft = left;
//最后一次 tempLeft = 0 ,right = 7
System.out.println("tempLeft=" + tempLeft + " " + "right=" + right);
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}