归并排序执行流程原理(图解):
JDK17代码实现:
package SortAlgorithm.MergerSort;
import java.util.Arrays;
public class MergerSort {
public static void main(String[] args) {
int[] arr = {456,10,78,3,19,28,54,321,-84,97};
int[] temp = new int[arr.length];
System.out.println("原数组: "+ Arrays.toString(arr));
MergerSort.mergerSort(arr,0,arr.length-1,temp);
System.out.println("排序后: "+ Arrays.toString(arr));
}
/**
* 归并排序
* @param arr 待排序数组
* @param left 合并时左子项的左边界
* @param right 合并时右子项的右边界(调用时应该传arr.length-1)
*/
public static void mergerSort(int[] arr,int left,int right,int[] temp){
//传入一个数组,先计算中间索引,用于分解数组(mid在回溯期间也是左子项的右边界)
int mid = (left+right) / 2;
//如果l==r 说明此时数组已经分解剩一个元素,只剩一个元素没必要比较,直接return返回到上一层栈帧
if (left == right) return;
//向左遍历,分解左边的数组
mergerSort(arr,left,mid,temp);
//向右遍历,分解右边的数组
mergerSort(arr,mid + 1,right,temp);
//能执行到下面的语句,说明已经开始回溯(即已经分解到只剩一个元素)
//下面开始合并操作
//l1,l2用来后续合并操作时遍历左右子项
//l1是左子项的左边界,l2是右子项的左边界
int l1 = left;
int l2 = mid + 1;
//创建一个指向temp数组的索引t,便于操作temp数组
int t = l1;
//l1和l2索引在边界内才允许遍历左右子项
while (l1 <= mid && l2 <= right) {
if (arr[l1] <= arr[l2]) {
temp[t] = arr[l1];
//左子项索引和temp索引同步后移
l1++;
t++;
}else {
temp[t] = arr[l2];
//右子项索引和temp索引同步后移
l2++;
t++;
}
}
//退出循环,说明l1或者l2索引其中一个出现越过边界的情况
//把没有越过边界的一边剩下的数据照搬到temp数组中
//li <= mid 说明是左子项的索引没越界,把左子项剩余的数据追加到temp
while (l1 <= mid) {
temp[t] = arr[l1];
l1++;
t++;
}
//l2 <= right 说明是右子项的索引没越界,把右子项剩余的数据追加到temp
while (l2 <= right) {
temp[t] = arr[l2];
l2++;
t++;
}
//上述操作完成后,temp数组内就有了这轮排序完成后的数据
//将这一轮排好序的temp数组赋值到原数组中,修改原数组数据
//将t复位,同步遍历temp数组和原数组,然后覆盖原数组对应索引的数据
t = left;
while (t <= right) {
//t既是原数组的索引,也是temp数组的索引,因为当初存到temp数组时就是对应着arr数组的索引存的
arr[t] = temp[t];
t++;
}
//修改原数组完成,该层栈帧结束,自动回溯到上一层栈帧
}
}
测试图: