【AcWing 787. 归并排序】
板子:【排序板子】
归并排序原理
有序序列的归并:利用有序序列的单调性质,设置双指针对序列进行扫描,合并后的序列保持单调性不变。
- 令 i i i , j j j 双指针分别指向两个递增序列 A A A , B B B 的首元素,设置新的空序列 C C C ;
- 若 A [ i ] A[i] A[i] 小于等于 B [ j ] B[j] B[j] ,则 A [ i ] A[i] A[i] 是当前序列 A A A 和 B B B 的剩余元素中最小的那个,把 A [ i ] A[i] A[i] 加入序列 C C C 中,并让 i i i 加 1;
- 若 A [ i ] A[i] A[i] 大于 B [ j ] B[j] B[j] ,则 B [ j ] B[j] B[j] 是当前序列 A A A 和 B B B 的剩余元素中最小的那个,把 B [ j ] B[j] B[j] 加入序列 C C C 中,并让 j j j 加 1;
- 重复第2,3步操作,直到双指针 i i i , j j j 有任意一个达到序列末端为止,然后将另一个序列剩下的所有元素依次归入序列 C C C 中。
归并排序:反复归并序列使其最终达到有序。归并排序需要设置一个和原数组大小一样的辅助数组。
- 把序列两两分组,将序列归并为 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil ⌈2n⌉ 个组,组内单独排序;
- 将 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil ⌈2n⌉ 个组两两归并为 ⌈ n 4 ⌉ \lceil \frac{n}{4} \rceil ⌈4n⌉ 个组,组内单独排序;
- 重复两两归并-单独排序过程,直到只剩下一个组为止
可参考:【归并排序动画演示】
C++
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int nums[N], tmps[N], n;
void mergeSort(int l, int r) {
if (l >= r) return; // 如果当前区间已经没有元素则返回
int mid = l + r >> 1, k = 0;
mergeSort(l, mid), mergeSort(mid + 1, r); // 递归排序左右两侧
int i = l, j = mid + 1; // 设置双指针
while (i <= mid && j <= r) // 将左右区间有序归并
if (nums[i] <= nums[j]) tmps[k ++] = nums[i ++];
else tmps[k ++] = nums[j ++];
while (i <= mid) tmps[k ++] = nums[i ++]; // 把左区间剩下部分归入新序列
while (j <= r) tmps[k ++] = nums[j ++]; // 把右区间剩下部分归入新序列
for (i = l, j = 0; i <= r; ++ i, ++ j) nums[i] = tmps[j]; // 把辅助数组的值归位到原数组
}
int main()
{
scanf("%d", &n);
for (auto i = 0; i < n; ++ i) scanf("%d ", &nums[i]);
mergeSort(0, n - 1);
for (auto i = 0; i < n; ++ i) printf("%d ", nums[i]);
return 0;
}