归并排序
736 字
4 分钟
归并排序

归并排序
归并排序也是分治算法。它和快速排序的区别是:快速排序先划分再递归,归并排序先递归再合并。
归并排序的核心思想是:
先把左右两半分别排好,再把两个有序数组合成一个有序数组。1、问题描述
给定数组:
5 3 8 4 2要求从小到大排序:
2 3 4 5 8如果把数组不断二分,最后每个小区间只有一个数。一个数天然有序,然后再一层层合并回来。
2、归并排序的基本步骤
1. 找到中点 mid2. 递归排序左半边 [l, mid]3. 递归排序右半边 [mid + 1, r]4. 用双指针把两个有序区间合并到临时数组5. 把临时数组复制回原数组核心是第 4 步:合并两个有序区间。
3、合并两个有序区间
假设左边是:
2 5 8右边是:
3 4 9合并时用两个指针:
i 指向左区间当前元素j 指向右区间当前元素谁小就先放进临时数组得到:
2 3 4 5 8 94、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];如果忘了这一步,递归上一层拿到的还是旧数组。
总结
归并排序可以记成:
二分 -> 排左边 -> 排右边 -> 合并两个有序区间它的模板比快速排序更稳定,也更适合继续扩展到“逆序对数量”等问题。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














