qqqqqqq
//整体思路
//归并排序的核心思想是 "分而治之":
//分:将数组不断分成两半,直到每个子数组只有一个元素
//治:将已排序的子数组合并,逐步得到更大的有序数组
//最终得到完全排序的数组
//以数组 {12, 11, 13, 5, 6, 7} 为例:
//首先分解为 {12, 11, 13} 和 {5, 6, 7}
//左半部分继续分解为 {12}, {11, 13},排序后合并为 {11, 12, 13}
//右半部分继续分解为 {5}, {6, 7},排序后合并为 {5, 6, 7}
//最终合并左右两部分得到 {5, 6, 7, 11, 12, 13}
public class qq {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return; // 数组为空或只有一个元素时无需排序
}
int n = arr.length;
int[] temp = new int[n]; // 临时数组,用于合并过程
sort(arr, 0, n - 1, temp);
}
// 这是归并排序的核心递归方法
// 首先计算中间位置,将数组分为左右两部分
// 递归排序左半部分(left 到 mid)
// 递归排序右半部分(mid+1 到 right)
// 最后调用 merge () 方法合并两个有序子数组
// 分治排序的递归方法
private static void sort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
// 1. 分解:将数组分成两部分
int mid = (left + right) / 2;
// 2. 递归解决:分别排序左右两部分
sort(arr, left, mid, temp);
sort(arr, mid + 1, right, temp);
// 3. 合并:将两个有序子数组合并
merge(arr, left, mid, right, temp);
}
}
// 这是归并排序中最关键的部分,负责合并两个有序子数组
// 使用三个指针分别跟踪左子数组、右子数组和临时数组的当前位置
// 先比较两个子数组的元素,将较小的元素放入临时数组
// 处理剩余元素(左右子数组可能有一个先遍历完)
// 最后将临时数组中的有序元素复制回原数组
// 合并两个有序子数组
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 左子数组的起始索引
int j = mid + 1; // 右子数组的起始索引
int k = left; // 临时数组的起始索引
// 比较两个子数组的元素,将较小的元素放入临时数组
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
// 将左子数组剩余元素复制到临时数组
while (i <= mid) {
temp[k++] = arr[i++];
}
// 将右子数组剩余元素复制到临时数组
while (j <= right) {
temp[k++] = arr[j++];
}
// 将临时数组中的元素复制回原数组
for (i = left; i <= right; i++) {
arr[i] = temp[i];
}
}
// 测试方法
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6, 7};
System.out.println("排序前的数组:");
for (int num : arr) {
System.out.print(num + " ");
}
mergeSort(arr);
System.out.println("\n排序后的数组:");
for (int num : arr) {
System.out.print(num + " ");
}
}
}
//时间复杂度:O (n log n),无论最好、最坏还是平均情况
//空间复杂度:O (n),需要额外的临时数组
//稳定性:稳定排序(相等元素的相对顺序保持不变)
//适合处理大规模数据,尤其适合链表排序
//归并排序通过牺牲空间换取了稳定的高效排序性能,是分治法在排序算法中的经典应用。