你可以在 https://visualgo.net/zh/sorting 的 MER 标签中看到归并排序的动画。
归并排序:
时间复杂度:O(nlogn)
空间复杂度:O(n)
基本思想:
分治思想。
8 6 2 3 1 5 7 4
分:8 6 2 3 | 1 5 7 4
分:8 6 | 2 3 | 1 5 | 7 4
分:8 | 6 | 2 | 3 | 1 | 5 | 7 | 4
并:8 | 6 | 2 | 3 | 1 | 5 | 7 | 4
并:6 8 | 2 3 | 1 5 | 4 7
并:2 3 6 8 | 1 4 5 7
并:1 2 3 4 5 6 7 8
首先在分的时候,对于一个 8 个数组数组,分解成了 O(log8) 也就是 3 层
每一层进行排序后向上合并即可。这个合并的过程就是合并两个有序的数组。
合并两个有序数组可以做到 O(n) 级别的合并。
LeetCode 的 88 题:https://leetcode-cn.com/problems/merge-sorted-array/
实现:
void __merge(int arr[], int l, int mid, int r) {
int tmp[r - l + 1];
for (int i = l; i <= r; ++i) {
tmp[i - l] = arr[i];
}
int i = l, j = mid + 1;
for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = tmp[j - l];
j++;
} else if (j > r) {
arr[k] = tmp[i - l];
} else if (tmp[i - l] < tmp[j - l]) {
arr[k] = tmp[i - l];
i++;
} else {
arr[k] = tmp[j - l];
j++;
}
}
}
void __mergeSort(int arr[], int l, int r) {
if (l >= r) return; // 也就是当分到只有一个元素的时候,结束
// 分
int mid = (l + r) / 2;
__mergeSort(arr, l, mid);
__mergeSort(arr, mid + 1, r);
// 并
__merge(arr, l, mid, r);
}
void mergeSort(int arr[], int n) {
__mergeSort(arr, 0, n-1);
}
优化
当元素已经有序了的时候,可以不再进行 __merge
操作。
void __mergeSort(int arr[], int l, int r) {
if (l >= r) return; // 也就是当分到只有一个元素的时候,结束
// 分
int mid = (l + r) / 2;
__mergeSort(arr, l, mid);
__mergeSort(arr, mid + 1, r);
// 并
if (arr[mid] > arr[mid + 1]) {
__merge(arr, l, mid, r);
}
}
测试:
100 k个随机数字:
归并排序 : 0.022311 s
插入排序 : 7.63138 s
100 k个接近有序数字:
归并排序 : 0.001231 s
插入排序 : 0.000419 s
可以看到,接近有序的情况下,归并排序的时间耗费比插入排序高。
因为在接近有序的情况下,归并排序也会进行分
操作,分的操作是 O(logn) 的。但是插入排序在接近有序情况下时间复杂度接近 O(n)。
EOF