排序算法:归并排序
思想:分治。先将子序列排好序后合并,区别于快速排序的先分割后排子序。
一、算法
递归地:将子序列两两合并为新的子序列,直到得到整个序列。
实现时需要额外的空间辅助(个人认为代码难写的点)。
合并两个有序子序列的伪代码(其实和合并链表一样):
Arr1, Arr2, MArr 分别代表需要合并的两个子序列,长度分别为L1、L2,以及存储合并后的序列,长度为L1+L2。
i <- 0, j <- 0, k <- 0 // 初始化三个数组的下标的指针
while i < L1 and j < L2 // 当两个子数组指针都还没有指到尾部
if Arr1[i] <= Arr2[j] // 将当前小的元素加入到合并数组中,更新指针
MArr[k] <- Arr1[i]
i <- i+1
k <- k+1
else
MArr[k] <- Arr2[j]
j <- j+1
k <- k+1
while i < L1 // 将剩余元素加入进合并数组
MArr[k] <- Arr1[i]
i <- i+1
k <- k+1
while j < L2
MArr[k] <- Arr2[j]
j <- j+1
k <- k+1
二、代码实现
#include <iostream>
using namespace std;
void merge(int Arr1[], int L1, int Arr2[], int L2, int MArr[]) { // 合并函数
int i = 0, j = 0, k = 0;
while (i < L1 && j < L2) {
if (Arr1[i] <= Arr2[j]) {
MArr[k] = Arr1[i];
i++;
k++;
}
else {
MArr[k] = Arr2[j];
j++;
k++;
}
}
while (i < L1) {
MArr[k] = Arr1[i];
i++;
k++;
}
while (j < L2) {
MArr[k] = Arr2[j];
j++;
k++;
}
}
void MergeSort(int Arr[], int n) { // 归并排序
if (n < 2) { // 终止条件:单个元素直接返回
return;
}
const int half_n = n / 2;
int* Arr1 = new int[half_n]; // 创建辅助空间存储子数组,然后就可以将后序合并好的数组存储到input数组中
int* Arr2 = new int[n - half_n];
for (int i = 0; i < half_n; i++) {
Arr1[i] = Arr[i];
}
for (int i = half_n; i < n; i++) {
Arr2[i - half_n] = Arr[i];
}
MergeSort(Arr1, half_n); // 子数组递归调用排好序后
MergeSort(Arr2, n - half_n);
merge(Arr1, half_n, Arr2, n-half_n, Arr); // 合并子数组
delete[] Arr1;
delete[] Arr2;
}
int main() {
int A[8] = {6,10,13,5,8,3,2,11};
MergeSort(A, 8);
for (int i = 0; i < 8; i++) {
cout << A[i] << ' ';
}
}
三、分析
在每次排序都额外需要拷贝一份子数组,空间复杂度为Θ(n)\Theta(n)Θ(n)。
每次归并都是将长度平半分的数组合并,且需要遍历一遍,即T(n)=2T(n/2)+Θ(n)T(n)=2T(n/2)+\Theta(n)T(n)=2T(n/2)+Θ(n),可知时间复杂度为Θ(nlgn)\Theta(nlgn)Θ(nlgn)。