1. 归并排序——分治
# 算法原理
归并排序的思想就是分治,先递归分解数组,再合并数组。
将数组分解到最小之后,再往上一层两两合并两个有序的数组,最终递归返回的就是一个排好序的数组。
递归分解的时间复杂度是O(logn),合并数组的时间复杂度是O(n),因此归并排序的时间复杂度就是O(nlogn)。
# 步骤
- 确定分界点
mid = (l + r) / 2
- 递归调用左右区间
- 一层层调用到底,从最底层归并
- 归并排序, 合二为一
# 如何实现合二为一?
合并的基本思路就是双指针算法,比较两个数组最前面的数字,谁小就先取谁,取了后相应的指针就往后移动一位。 然后再比较,直到一个数组为空,最后把一个数组的剩余部分复制过来即可。
- 找两个指针中较小的一个数,把值写入
res[]
,然后指针右移 - 直到其中一个序列的指针指向最后一个
- 把另一个序列的指针剩下的数依次写入
res[]
# 代码模板
const int N = 10e5 + 10;
int temp[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, j = mid + 1, k = 0;
while(i <= mid && j <= r) {
if( q[i] <= q[j] ) temp[k++] = q[i++];
else temp[k++] = q[j++];
}
while(i <= mid) temp[k++] = q[i++];
while(j <= r) temp[k++] = q[j++];
for(i = l, j = 0; i <= r; i++, j++) q[i] = temp[j];
}
# 总结
归并排序没有太多复杂的边界问题,核心思想是分治,只要理解了这一点,基本上原理就弄清楚了。
在进行归并排序之前,先递归调用左右区间,可以理解为将数组二分分解。
调用到最深一层也就是区间里只有一个元素的时候,开始执行递归函数的返回,也就是执行归并的过程。
归并的过程可以如上图所示,每一层的都是把2个相邻的区间通过双指针算法排序并合并到一个区间(合并这个动作由递归返回发生),每次递归返回时区间都是有序的,最终合并回一个区间,完成排序。
需要注意的是,归并排序的时间复杂度是O(nlogn),但是中间过程需要用到一个数组来存放临时排序的结果,所以空间复杂度是O(n)。
2022.4.10