1. 归并排序
几个高级排序算法之一,具体原理可以参考这个视频:归并排序算法讲解。基本原理就是使用分治的思想,将数组不断二分,分割成单个元素,然后从单个元素开始两两合并,将两个有序的数组片段合并为一个新的有序片段。最后将整个数组归为有序。
基本步骤:
- 将数组不断二分,分割成只包含单个元素的片段;
- 对分割之后的片段进行两两合并,将两个片段合并为一个有序的大片段,最底层是对于单个元素的片段进行合并,单个元素肯定有序,合并只需要根据两个元素的大小进行片段的重排。需要使用额外的内存空间保存两个子片段,然后在原数组上存放合并之后的片段。
- 从最小的片段开始,归并到整个数组有序。
由上面的过程,知道归并排序需要使用递归来完成,先分割,分割到最底层了,然后开始合并。
因此递归函数可以这样写:
void MergeSort(vector<int>& nums, int start, int end, vector<int>& tmp) {
if (nums.empty() || start >= end)return;
int mid = start + (end - start) / 2;
//将待排序区间从中间分割开
MergeSort(nums, start, mid, tmp);
MergeSort(nums, mid + 1, end, tmp);
//将分割开的元素进行合并
Merge(nums, start, mid, end, tmp);
}
其中Merge()函数是核心,负责将两个片段进行合并。Merge()函数中需要先将两个片段拷贝一份,然后按顺序将两个片段的内容写到源地址上。
/**实现具体归并的函数
*
*该函数将两个有序的数组进行合并,合并为一个有序的数组
*传入参数:第一个已排序序列[start, mid];第二个已排序序列[mid+1, end];临时存储数组tmp
*/
void Merge(vector<int>& nums, int start, int mid, int end, vector<int>& tmp) {
int firstIdx = start;
int secondIdx = mid + 1;
int numsIdx = start;
//先将两个已排序序列复制到tmp中
for (int i = start; i <= mid; ++i)tmp[i] = nums[i];
for (int i = mid + 1; i <= end; ++i)tmp[i] = nums[i];
//然后将tmp中的两段数组合并到nums中
while (numsIdx <= end) {
if (secondIdx > end)nums[numsIdx++] = tmp[firstIdx++];
else if (firstIdx > mid)nums[numsIdx++] = tmp[secondIdx++];
else {
if (tmp[firstIdx] <= tmp[secondIdx])nums[numsIdx++] = tmp[firstIdx++];
else nums[numsIdx++] = tmp[secondIdx++];
}
}
}
带输入的完整程序如下:
// MergeSort_Vector.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
using namespace std;
void Sort(vector<int>& nums);
void MergeSort(vector<int>& nums, int start, int end, vector<int>& tmp);
void Merge(vector<int>& nums, int start, int mid, int end, vector<int>& tmp);
void printArray(vector<int>& nums);
int main()
{
vector<int> a({ 5,6,1,8,3,4,9,7,2,3 });
//打印a的初始值
cout << "Initial value of vecotr a:";
printArray(a);
Sort(a);
//打印排序后的a
cout << "Sorted value of vecotr a:";
printArray(a);
return 0;
}
//通用接口
void Sort(vector<int>& nums) {
vector<int> tmp(nums.size());
MergeSort(nums, 0, nums.size() - 1, tmp);
}
//归并排序的入口
void MergeSort(vector<int>& nums, int start, int end, vector<int>& tmp) {
if (nums.empty() || start >= end)return;
int mid = start + (end - start) / 2;
//将待排序区间从中间分割开
MergeSort(nums, start, mid, tmp);
MergeSort(nums, mid + 1, end, tmp);
//将分割开的元素进行合并
Merge(nums, start, mid, end, tmp);
}
/**实现具体归并的函数
*
*该函数将两个有序的数组进行合并,合并为一个有序的数组
*传入参数:第一个已排序序列[start, mid];第二个已排序序列[mid+1, end];临时存储数组tmp
*/
void Merge(vector<int>& nums, int start, int mid, int end, vector<int>& tmp) {
int firstIdx = start;
int secondIdx = mid + 1;
int numsIdx = start;
//先将两个已排序序列复制到tmp中
for (int i = start; i <= mid; ++i)tmp[i] = nums[i];
for (int i = mid + 1; i <= end; ++i)tmp[i] = nums[i];
//然后将tmp中的两段数组合并到nums中
while (numsIdx <= end) {
if (secondIdx > end)nums[numsIdx++] = tmp[firstIdx++];
else if (firstIdx > mid)nums[numsIdx++] = tmp[secondIdx++];
else {
if (tmp[firstIdx] <= tmp[secondIdx])nums[numsIdx++] = tmp[firstIdx++];
else nums[numsIdx++] = tmp[secondIdx++];
}
}
}
//vector的打印函数
void printArray(vector<int>& nums) {
for (auto it = nums.begin(); it != nums.end();++it)cout << *it << " ";
cout << endl;
}
2. 使用函数模板和函数对象实现通用的归并排序算法
使用函数模板可以将上面的针对int类型的归并排序拓展到不同的数据类型,使用函数对象可以将自定义的比较函数传入归并排序的算法中,实现对自定义数据类型的排序。对于基本数据类型,可以使用STL中自带的less和greater函数对象。
对排序函数使用函数对象进行元素之间的比较,默认使用STL的less,即实现升序排序。
完整代码如下:
/**
* @file MergeSort.cpp
* @brief 归并排序的基本原理就是使用分治的思想,将数组不断二分,分割成单个元素,然后从单个元素开始两两合并,将两个有序的数组片段合并为一个新的有序片段。最后将整个数组归为有序。
1. 将数组不断二分,分割成只包含单个元素的片段;
2. 对分割之后的片段进行两两合并,将两个片段合并为一个有序的大片段,最底层是对于单个元素的片段进行合并,单个元素肯定有序,合并只需要根据两个元素的大小进行片段的重排。
需要使用额外的内存空间保存两个子片段,然后在原数组上存放合并之后的片段。
3. 从最小的片段开始,归并到整个数组有序。
* @author 好好学习
* @version v1
* @date 2020-08-08
*/
#include <iostream>
#include <vector>
#include <queue>
#include <string>
using namespace std;
template<typename T, typename _compare = std::less<T> > //通用接口
void MergeSort(vector<T>& nums, _compare cmp = std::less<T>());
template<typename T, typename _compare = std::less<T>> //归并排序的入口
void MSort(vector<T>& nums, int start, int end, vector<T>& tmp, _compare cmp = std::less<T>());
template<typename T, typename _compare = std::less<T>> //实现具体归并的函数
void Merge(vector<T>& nums, int start, int mid, int end, vector<T>& tmp, _compare cmp = std::less<T>());
template<typename T> //vector的打印函数
void printArray(vector<T>& nums);
/**
* @brief 自定义仿函数,作为比较函数
* @return bool 返回比较结果
*/
template<typename T>
struct cmp {
bool operator()(T& a, T& b) {
return a < b;
}
};
int main() {
vector<int> a({ 5,6,1,8,3,4,9,7,2,3 });
vector<string> b({ "hello","world","hahaha", "welcome", "goodbye","nice","apple" });
//打印a和b的初始值
cout << "Initial value of vecotr a:";
printArray<int>(a);
cout << "Initial value of vecotr b:";
printArray<string>(b);
//使用STL中的比较函数,less:升序排列;greator:降序排列
MergeSort<int>(a, less<int>());
//使用自定义的比较函数,可以支持自定义数据类型(或者针对自定义数据类型,重载"<"或">"运算符,可以使用less或者greater
MergeSort<string>(b, cmp<string>());
//打印排序后的a和b
cout << "Sorted value of vecotr a:";
printArray<int>(a);
cout << "Sorted value of vecotr b:";
printArray<string>(b);
system("pause");
return 0;
}
/**
* @brief 归并排序的通用接口
* @param[in] nums 待排序元素序列
* @param[in] cmp 用于指定比较方式的函数对象,默认使用less<T>
* @return
*/
template<typename T, typename _compare >
void MergeSort(vector<T>& nums, _compare cmp) {
vector<T> tmp(nums.size());
MSort(nums, 0, nums.size() - 1, tmp, cmp);
}
/**
* @brief 归并排序的入口
* @param[in] nums 待排序元素序列
* @param[in] cmp 用于指定比较方式的函数对象,默认使用less<T>
* @param[in] start 归并区间的左边界
* @param[in] end 归并区间的右边界(都是闭区间)
* @param[in] tmp 用于保存临时数据的临时容器
* @return
*/
template<typename T, typename _compare>
void MSort(vector<T>& nums, int start, int end, vector<T>& tmp, _compare cmp) {
if (nums.empty() || start >= end)return;
int mid = start + (end - start) / 2;
//将待排序区间从中间分割开
MSort(nums, start, mid, tmp, cmp);
MSort(nums, mid + 1, end, tmp, cmp);
//将分割开的元素进行合并
Merge(nums, start, mid, end, tmp, cmp);
}
/**
* @brief 实现具体归并的函数,该函数将两个有序的序列()进行合并,合并为一个更大的有序序列
* @param[in] nums 待排序元素序列
* @param[in] cmp 用于指定比较方式的函数对象,默认使用less<T>
* @param[in] start,mid,end 指定两个待合并区间的边界:[start, mid]和[mid+1, end]
* @param[in] tmp 用于保存临时数据的临时容器
* @return
*/
template<typename T, typename _compare>
void Merge(vector<T>& nums, int start, int mid, int end, vector<T>& tmp, _compare cmp) {
int firstIdx = start;
int secondIdx = mid + 1;
int numsIdx = start;
//先将两个已排序序列复制到tmp中
for (int i = start; i <= mid; ++i)tmp[i] = nums[i];
for (int i = mid + 1; i <= end; ++i)tmp[i] = nums[i];
//然后将tmp中的两段数组合并到nums中
while (numsIdx <= end) {
if (secondIdx > end)nums[numsIdx++] = tmp[firstIdx++];
else if (firstIdx > mid)nums[numsIdx++] = tmp[secondIdx++];
else {
if (cmp(tmp[firstIdx], tmp[secondIdx]))nums[numsIdx++] = tmp[firstIdx++];
else nums[numsIdx++] = tmp[secondIdx++];
}
}
}
//vector的打印函数
/**
* @brief vector序列打印输出函数
* @param[in] nums 待输出序列
* @return
*/
template<typename T>
void printArray(vector<T>& nums) {
for (auto it = nums.begin(); it != nums.end(); ++it)cout << *it << " ";
cout << endl;
}