归并排序

736 字
4 分钟
归并排序

归并排序#

归并排序也是分治算法。它和快速排序的区别是:快速排序先划分再递归,归并排序先递归再合并。

归并排序的核心思想是:

先把左右两半分别排好,再把两个有序数组合成一个有序数组。

1、问题描述#

给定数组:

5 3 8 4 2

要求从小到大排序:

2 3 4 5 8

如果把数组不断二分,最后每个小区间只有一个数。一个数天然有序,然后再一层层合并回来。

2、归并排序的基本步骤#

1. 找到中点 mid
2. 递归排序左半边 [l, mid]
3. 递归排序右半边 [mid + 1, r]
4. 用双指针把两个有序区间合并到临时数组
5. 把临时数组复制回原数组

核心是第 4 步:合并两个有序区间。

3、合并两个有序区间#

假设左边是:

2 5 8

右边是:

3 4 9

合并时用两个指针:

i 指向左区间当前元素
j 指向右区间当前元素
谁小就先放进临时数组

得到:

2 3 4 5 8 9

4、C++ 模板#

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r) {
if (l >= r) return;
int mid = (l + r) >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int i = l;
int j = mid + 1;
int k = 0;
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、为什么归并排序稳定#

看这句:

if (q[i] <= q[j]) tmp[k++] = q[i++];

当左右两个数相等时,先放左边的元素。这样相等元素的原始相对顺序不会被打乱,所以归并排序是稳定排序。

6、复杂度#

每一层合并所有元素,总共是 O(n)。

数组每次二分,层数是 O(log n)。

所以总时间复杂度:

O(n log n)

额外空间复杂度:

O(n)

因为需要临时数组 tmp。

7、容易写错的地方#

(1)递归右区间从 mid + 1 开始#

merge_sort(q, mid + 1, r);

不要写成 merge_sort(q, mid, r),否则区间可能无法缩小。

(2)剩余元素要继续复制#

while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];

两个区间不一定同时走完。

(3)最后要复制回原数组#

q[i] = tmp[j];

如果忘了这一步,递归上一层拿到的还是旧数组。

总结#

归并排序可以记成:

二分 -> 排左边 -> 排右边 -> 合并两个有序区间

它的模板比快速排序更稳定,也更适合继续扩展到“逆序对数量”等问题。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

归并排序
https://blog.tjcheng.cn/posts/migrated/old-75/
作者
TengJinCheng
发布于
2025-09-28
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录