归并排序算法思想主要是将两个有序数组合并成为一个有序数组,其关键点采用“分治思想”,“分”是二分法,将无序数组分割成为一个个有序数组,然后通过“治” 将两个有序数组合并成为一个有序数组。时间时间复杂度为为O(nlogn),空间复杂度是O(n)的常量级临时变量,是稳定排序算法。
归并排序主要实现思想:
采用“分治思想”,先“分”将整个无序数组先左边递归,再右边递归,类似二叉树先序遍历方法,将数组分为一个个有序数组;然后通过“治”将一个个有序数组通过合并merge方法组合成一个有序数组;
1.“分”vector<int> merge(vector<int>& nums, int start, int end);从起始和终止位置开始进行二分法。
vector<int> Sorts::merge(vector<int>& nums, int start, int end)
{
if (start == end) //递归终止条件:如果起始下标等于结束下标,则终止递归
return { nums[start] }; // 返回左边第一个元素
int middle = (start + end) / 2; // 分治法,采用二分法,取中间下标开始左右递归,直到一个元素开始返回
auto left = merge(nums, start, middle); // 先递归左边部分,退出该层后向右递归,直到一个元素
print(left);
auto right = merge(nums, middle + 1, end); // 先左边再右边递归取有序数组
return merge(left, right); // 合并两个有序数组,然后继续回退到上一层左边或者右边递归,直到整个递归结束返回为止
}
一直遍历左边部分数据,然后再返回上层遍历右边数据,这样每次左右部分个子得到一个个或组合后的有序数据组。
即下图1所示,每次都打印遍历后左边的值,可以分析出,这个递归类似二叉树里面的先序遍历,每次总是先遍历左边元素回退上一层后再遍历右边最里层元素,图2打印数据更加印证这先遍历左边再遍历右边顺序。
![](https://img-blog.csdnimg.cn/f8d47c554bc3473797a20393cc8f0e79.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5LiJ5YWs5a2QVGpx,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/a672326e149a4203b3b0b1e0afc80cdc.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5LiJ5YWs5a2QVGpx,size_20,color_FFFFFF,t_70,g_se,x_16)
2.步骤1将无须数组分为一个个或一块块有序数组,这时需要将两个有序数组依次合并成一个有序数组vector<int> merge(vector<int>& nums1, vector<int>& nums2);
合并的关键是用两个游走下标分别遍历两个数组,将其中一个较小元素放入新创建的数组里;当其中一个数组遍历完毕后,将剩余数组放入新数组后面则完成排序;如下代码所示:
vector<int> Sorts::merge(vector<int>& nums1, vector<int>& nums2)
{
int i1{ 0 }, i2{ 0 };
vector<int> mergeArr(nums1.size() + nums2.size(), 0);
while (i1 < nums1.size() && i2 < nums2.size()) // 分别用两个下标遍历两个数组,取较小元素放到新数组,直到其中一个数组遍历完毕为止
{
//mergeArr[i1 + i2] = nums1[i1] <= nums2[i2] ? nums1[i1++] : nums2[i2++]; 这行在linux下可以,在msvc编译器段错误
if (nums1[i1] <= nums2[i2]) // 取较小元素放入新数组
{
mergeArr[i1 + i2] = nums1[i1]; //
++i1; // msvc ++ 好像跟gcc编译器的实现不一样,这里放在数组里面++会段错误
}
else
{
mergeArr[i1 + i2] = nums2[i2];
++i2;
}
}
while (i1 < nums1.size()) // 将剩余数组放入新数组后面
{
mergeArr[i1 + i2] = nums1[i1];
++i1;
}
while (i2 < nums2.size()) // 将剩余数组放入新数组后面
{
mergeArr[i1 + i2] = nums2[i2];
++i2;
}
return mergeArr;
}
3.下面是完整的示例代码:
Sorts.h
#pragma once
#include <iostream>
#include <vector>
using namespace std;
struct Sorts {
void merge(vector<int>& nums);
void print(vector<int>& nums);
private:
vector<int> merge(vector<int>& nums, int start, int end);
vector<int> merge(vector<int>& nums1, vector<int>& nums2);
};
Sorts.cpp
#include "Sorts.h"
void Sorts::merge(vector<int>& nums)
{
if (nums.size() <= 1)
{
return;
}
auto tmpNums = merge(nums, 0, nums.size() - 1);
nums = tmpNums;
}
void Sorts::print(vector<int>& nums)
{
for (const auto& it : nums)
cout << it << ",";
cout << endl;
}
vector<int> Sorts::merge(vector<int>& nums, int start, int end)
{
if (start == end) //递归终止条件:如果起始下标等于结束下标,则终止递归
return { nums[start] }; // 返回左边第一个元素
int middle = (start + end) / 2; // 分治法,采用二分法,取中间下标开始左右递归,直到一个元素开始返回
auto left = merge(nums, start, middle); // 先递归左边部分,退出该层后向右递归,直到一个元素
auto right = merge(nums, middle + 1, end); // 先左边再右边递归取有序数组
return merge(left, right); // 合并两个有序数组,然后继续回退到上一层左边或者右边递归,直到整个递归结束返回为止
}
vector<int> Sorts::merge(vector<int>& nums1, vector<int>& nums2)
{
int i1{ 0 }, i2{ 0 };
vector<int> mergeArr(nums1.size() + nums2.size(), 0);
while (i1 < nums1.size() && i2 < nums2.size()) // 分别用两个下标遍历两个数组,取较小元素放到新数组,直到其中一个数组遍历完毕为止
{
//mergeArr[i1 + i2] = nums1[i1] <= nums2[i2] ? nums1[i1++] : nums2[i2++]; 这行在linux下可以,在msvc编译器段错误
if (nums1[i1] <= nums2[i2]) // 取较小元素放入新数组
{
mergeArr[i1 + i2] = nums1[i1]; //
++i1; // msvc ++ 好像跟gcc编译器的实现不一样,这里放在数组里面++会段错误
}
else
{
mergeArr[i1 + i2] = nums2[i2];
++i2;
}
}
while (i1 < nums1.size()) // 将剩余数组放入新数组后面
{
mergeArr[i1 + i2] = nums1[i1];
++i1;
}
while (i2 < nums2.size()) // 将剩余数组放入新数组后面
{
mergeArr[i1 + i2] = nums2[i2];
++i2;
}
return mergeArr;
}
main.cpp
#include <vector>
#include "Sorts.h"
using namespace std;
int main()
{
vector<int> nums = { 2,0,1,6,8,10,5,99,87,333,2,0,1 };
Sorts sorts;
sorts.print(nums);
sorts.merge(nums);
sorts.print(nums);
return 1;
}
输出结果: