归并排序的学习
归并排序
思想:
归并的本质也是分治,不过不同于快速排序,它在将大问题分成小问题之后最后需要将小问题合并成最终的排序结果。
#include<iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N],tmp[N];
void merge_sort(int q[], int l, int r) {
if (l >= r) return; // 递归基准:如果子数组只有一个元素或为空,不需要排序。
int mid = (r + l) / 2; // 找到数组的中间索引
merge_sort(q, l, mid); // 递归排序左半部分
merge_sort(q, mid + 1, r); // 递归排序右半部分
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r) {
// 合并操作:从两个已排序的子数组中分别取出最小的元素放入临时数组 tmp
if (q[i] < q[j]) {
tmp[k++] = q[i++];
} else {
tmp[k++] = q[j++];
}
}
// 将剩余的元素复制到 tmp 中
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
// 将 tmp 中排好序的元素复制回原数组 q 中
for (i = l, j = 0; i <= r; i++, j++) {
q[i] = tmp[j];
}
}
int main() {
scanf("%d", &n); // 输入数组的大小
for (int i = 0; i < n; i++) scanf("%d", &q[i]); // 输入数组的元素
merge_sort(q, 0, n - 1); // 调用归并排序函数,对数组进行排序
for (int i = 0; i < n; i++) printf("%d\t", q[i]); // 输出排序后的数组
return 0;
}
6. 举例说明:
假设有一个数组 q = [38, 27, 43, 3, 9, 82, 10],我们进行归并排序。
第一次分解: 数组被分成两部分 [38, 27, 43] 和 [3, 9, 82, 10]。
递归分解到最小: 每个子数组继续分解,直到子数组长度为1:
[38, 27, 43] 分解成 [38], [27, 43],再分解为 [27], [43]。
[3, 9, 82, 10] 分解成 [3, 9], [82, 10],再分解为 [3], [9] 和 [82], [10]。
合并: 经过递归合并后,数组逐步恢复为有序状态:
合并 [27] 和 [43],得到 [27, 43]。
合并 [3] 和 [9],得到 [3, 9]。
合并 [82] 和 [10],得到 [10, 82]。
合并 [38] 和 [27, 43],得到 [27, 38, 43]。
合并 [3, 9] 和 [10, 82],得到 [3, 9, 10, 82]。
最终合并: 最后,合并 [27, 38, 43] 和 [3, 9, 10, 82],得到排序好的数组 [3, 9, 10, 27, 38, 43, 82]。
时间复杂度
归并排序的时间复杂度为 O(n log n),其中:
log n 是由于每次将数组分为两部分。
n 是因为每一层递归都需要遍历一次整个数组来进行合并。
所以,在最坏情况下,归并排序的时间复杂度是 O(n log n),对于任何情况都稳定在这个时间复杂度。