归并排序算法详解(c++ 版 递归实现)
一、归并排序(递归实现)的算法思想简介
一、将长度为n序列 递归拆解成n/2 个子序列 进行两两排序(递归归并左边的子序列,递归归并右边的子序列) 直到得到n个长度为1的自然有序序列为止
二、将两个已排序的子序列进行排序合并 直到有序序列的的长度为n 结束
三、图解如下:
二、排序过程的详解
1.将长度为n序列 递归拆解
- 假设有序列初始值为: {5,4,6,3,1,2,8,7,10}; int arr[] = {5,4,6,3,1,2,8,7,10};
- 取中间分解点 int mid = (height+low)/2 。其中:low 为序列的起始下标 hight 为结束下标 分解点取他们中间的点
- 递归排序 arr[low] 至 arr[mid] 区间的序列 (原序列的左侧部分)
- 递归排序 arr[mid+1] 至 arr[hight ] 区间的序列 (原序列的右侧部分)
- 将左边的有序的子序列 和 右边的有序子序列 排序后组合 逐个填写到原数组中(这个部分的详细内容看第二点)
- 直到被分解的子序列长度为1 的时候 自然有序 停止递归
- 代码如下:
/**
*功能:拆分有序的序列两两排序-拆解结束的条件 子序列长度为1的时候
*/
void sortArr(int arr[], int low, int hight) {
if (low < hight) {
int mid = (hight + low) / 2;
sortArr(arr,low,mid);// 递归拆解左边的序列
sortArr(arr, mid + 1, hight);// 递归拆解左边的序列
mergeArr(arr, low, mid, hight);// 将两个有序的子序列(arr[low至mid]、arr[mid+1至hight] 排序合并成一个新的有序列
}
}
实际上当他们拆分成1个有序的子序列的时候才开始调用mergeArr 进行排序 合并 然后得到新的有序的序列(长度为2)然后返回上一层得再进行排序
2.合并排序
- 首先要确定要排序两个有序的子序列的范围 所以 我们要传入low、mid、hight 来加以确定
- 得出 arr[low] 至 arr[mid] 为第一个有序的子序列的范围 arr[mid+1] 至 arr[hight] 为第二个有序的子序列范围
- 定义两个变量 int i; int j; 分别指向第一个和第二个有序子序列的开始部分 如下图所示 假设 此时待合并排序的第一个有序的子序列为 1,3,4,5,6 第二个有序的子序列为2,7,8,10 则变量i 指向的是1 变量j 指向的是 2 如下图所示(这个图是由博主自己画的有点渣渣 别介意)
- 定义一个指向长度为hight-low+1的数组的指针 int* tempArr = new int[hight - low + 1];
- 定义一个初始变量k 初始值为0 记录tempArr的实际长度即tempArr 目前有几条数据
- 假设排序是从小到大的排序
- 循环比较 有序的子序列 arr[low] 至 arr[mid] 以及 arr[mid+1] 至 arr[hight] 的值 最小的部分存入 tempArr 中
- 当i 超过第一个序列的边界的的时候 或则j 大于第二个序列的编辑的时候停止比较
- 如果i<=mid 意味着 arr[low] 到 arr[mid] 区间中的数组还没有比较完成 ,则直接把剩余的元素(arr[i]至arr[mid])复制到tempArr 中
- 如果j<=hight意味着 arr[mid+1] 到 arr[hight] 区间中的数组还没有比较完成 ,则直接把剩余的元素(arr[j]至arr[hight])复制到tempArr 中 相关代码如下:
- int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= hight) {
if (arr[i] < arr[j]) {
tempArr[k] = arr[i];
i++;
}
else {
tempArr[k] = arr[j];
j++;
}
k++;
}
// 如果 arr[low] 到 arr[mid] 区间中的数组还没有比较完成 ,直接复制到tempArr 中
while (i <= mid) {
tempArr[k] = arr[i];
i++;
k++;
}
// 如果 arr[mid+1] 到 arr[hight] 区间中的数组还没有比较完成 ,直接复制到tempArr 中
while (j <= hight) {
tempArr[k] = arr[j];
j++;
k++;
}
- 最后一步 将合并后的数组 tempArr 逐个赋值到 arr 数组中 赋值的范围为 low -hight因为子序列在arr 的范围是low-hight 所以arr 可变动的范围就是 arr[low]-arr[hight]
- 合并函数的完整代码如下:
void mergeArr(int arr[], int low, int mid, int hight) {
int* tempArr = new int[hight - low + 1];
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= hight) {
if (arr[i] < arr[j]) {
tempArr[k] = arr[i];
i++;
}
else {
tempArr[k] = arr[j];
j++;
}
k++;
}
// 如果 arr[low] 到 arr[mid] 区间中的数组还没有比较完成 ,直接复制到tempArr 中
while (i <= mid) {
tempArr[k] = arr[i];
i++;
k++;
}
// 如果 arr[mid+1] 到 arr[hight] 区间中的数组还没有比较完成 ,直接复制到tempArr 中
while (j <= hight) {
tempArr[k] = arr[j];
j++;
k++;
}
// 比较完成之后 将原本的数组arr 下标 low-hight 对应的内容 进行改变
i = low;
for (int tempK = 0;((tempK < k)&&(i<=hight));tempK++) {
arr[i] = tempArr[tempK];
i++;
}
delete[] tempArr;
tempArr = NULL;
}
三、 归并排序代码详情
归并代码实际上就是两两个拆分 拆分到单个之后 再逐级排序合并 他的算法的空间复杂度为On 时间复杂度为nlog2n
#include <iostream>
using namespace std;
/**
*功能:将两个有序的数组合并成一个整体有序的数组
*/
void mergeArr(int arr[], int low, int mid, int hight) {
int* tempArr = new int[hight - low + 1];
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= hight) {
if (arr[i] < arr[j]) {
tempArr[k] = arr[i];
i++;
}
else {
tempArr[k] = arr[j];
j++;
}
k++;
}
// 如果 arr[low] 到 arr[mid] 区间中的数组还没有比较完成 ,直接复制到tempArr 中
while (i <= mid) {
tempArr[k] = arr[i];
i++;
k++;
}
// 如果 arr[mid+1] 到 arr[hight] 区间中的数组还没有比较完成 ,直接复制到tempArr 中
while (j <= hight) {
tempArr[k] = arr[j];
j++;
k++;
}
// 比较完成之后 将原本的数组arr 下标 low-hight 对应的内容 进行改变
i = low;
for (int tempK = 0;((tempK < k)&&(i<=hight));tempK++) {
arr[i] = tempArr[tempK];
i++;
}
delete[] tempArr;
tempArr = NULL;
}
/**
*功能:拆分有序的序列两两排序-拆解结束的条件 子序列长度为1的时候
*/
void sortArr(int arr[], int low, int hight) {
if (low < hight) {
int mid = (hight + low) / 2;
sortArr(arr,low,mid);// 递归拆解左边的序列
sortArr(arr, mid + 1, hight);// 递归拆解左边的序列
mergeArr(arr, low, mid, hight);// 将两个有序的子序列(arr[low至mid]、arr[mid+1至hight] 排序合并成一个新的有序列
}
}
int main() {
int arr[] = {5,4,6,3,1,2,8,7,10};
cout << "before sort " << endl;
for (int j = 0;j < 9;j++) {
cout << arr[j] << " ";
}
sortArr(arr,0,8);
cout << endl << "after sort " << endl;
for (int j = 0;j <9;j++) {
cout << arr[j] << " ";
}
return 0;
}
四、运行结果截图
如果有什么问题需要指正 欢迎在文章底下 友善 的留言 指正 一起进步一起成长。