题解:AcWing 787 归并排序
【题目来源】
AcWing:787 归并排序 - AcWing题库
【题目描述】
给定你一个长度为 \(n\) 的整数数列。
请你使用归并排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
【输入】
输入共两行,第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数(所有整数均在 \(1\sim 10^9\) 范围内),表示整个数列。
【输出】
输出共一行,包含 \(n\) 个整数,表示排好序的数列。
【输入样例】
5
3 1 2 4 5
【输出样例】
1 2 3 4 5
【核心思想】
-
问题分析:给定长度为 \(n\) 的整数序列,需要将其按从小到大排序。归并排序是一种基于分治思想的稳定排序算法,通过递归将序列二分,分别排序后再合并两个有序子序列。
-
算法选择:
- 归并排序(Merge Sort):采用分治策略,时间复杂度稳定为 \(O(n \log n)\)
- 双路归并:使用临时数组将两个已排序的子数组合并为一个有序数组
- 递归分解:不断将当前区间二分,直到区间长度为 \(1\)(天然有序),再逐层合并
-
关键步骤:
- 递归分解:
- 若区间长度 \(\leq 1\)(\(l \geq r\)),直接返回
- 计算中点 \(mid = \lfloor \frac{l + r}{2} \rfloor\)
- 递归排序左半区间 \([l, mid]\) 和右半区间 \([mid+1, r]\)
- 合并两个有序子数组:
- 初始化指针:\(i = l\)(左半部分),\(j = mid + 1\)(右半部分),\(k = 0\)(临时数组)
- 当 \(i \leq mid\) 且 \(j \leq r\) 时:
- 若 \(q[i] \leq q[j]\),\(\text{tmp}[k++] = q[i++]\)
- 否则 \(\text{tmp}[k++] = q[j++]\)
- 将左半部分或右半部分的剩余元素依次放入临时数组
- 将临时数组中的元素复制回原数组对应位置
- 递归分解:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),每层递归合并 \(O(n)\),共 \(\log n\) 层
- 空间复杂度:\(O(n)\),需要额外临时数组存储合并结果
-
归并排序的核心思想:
- 分治策略:将大问题分解为两个规模减半的子问题,递归求解后通过合并得到原问题的解。分解过程形成一棵高度为 \(\log n\) 的递归树
- 有序子数组的线性合并:两个已排序的子数组合并时,只需双指针线性扫描,每次选择较小者放入临时数组,合并过程 \(O(n)\) 且稳定(相等元素保持原有相对顺序)
- 稳定性保证:当 \(q[i] = q[j]\) 时优先取左半部分元素,确保相等元素的原始相对顺序不变,这是归并排序相较于快速排序的重要优势
- 时间复杂度稳定性:无论输入数据是否有序,归并排序始终进行 \(\log n\) 层分解和每层 \(O(n)\) 的合并,最坏、平均、最好时间复杂度均为 \(O(n \log n)\)
- 适用于需要稳定排序的场景、大规模数据排序、以及作为求解逆序对等问题的基础算法
【解题思路】

【算法标签】
归并排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010; // 定义数组最大容量
int n; // 数组元素个数
int q[N]; // 存储待排序的数组
int tmp[N]; // 归并排序使用的临时数组
// 归并排序函数
void merge_sort(int q[], int l, int r) {
// 递归终止条件:区间长度小于1时不需要排序
if (l >= r) return;
// 计算中点,分界点
int mid = (l + r) >> 1; // 等价于(l + r)/2
// 递归排序左右两半
merge_sort(q, l, mid); // 排序左半部分
merge_sort(q, mid + 1, r); // 排序右半部分
// 合并两个有序数组
int k = 0; // 临时数组的指针
int i = l; // 左半部分的指针
int j = mid + 1; // 右半部分的指针
// 比较左右两部分的元素,按顺序放入临时数组
while (i <= mid && j <= r) {
if (q[i] <= q[j]) tmp[k++] = q[i++]; // 左边元素较小
else tmp[k++] = q[j++]; // 右边元素较小
}
// 处理剩余元素(左半部分或右半部分可能还有剩余)
while (i <= mid) tmp[k++] = q[i++]; // 左半部分剩余元素
while (j <= r) tmp[k++] = q[j++]; // 右半部分剩余元素
// 将临时数组中的有序元素复制回原数组
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 ", q[i]);
return 0;
}
【运行结果】
5
3 1 2 4 5
1 2 3 4 5
浙公网安备 33010602011771号