C++——归并排序,从固定数据类型到函数模板以及使用函数对象自定义递增/递减

1. 归并排序

几个高级排序算法之一,具体原理可以参考这个视频:归并排序算法讲解。基本原理就是使用分治的思想,将数组不断二分,分割成单个元素,然后从单个元素开始两两合并,将两个有序的数组片段合并为一个新的有序片段。最后将整个数组归为有序。

基本步骤:

  1. 将数组不断二分,分割成只包含单个元素的片段;
  2. 对分割之后的片段进行两两合并,将两个片段合并为一个有序的大片段,最底层是对于单个元素的片段进行合并,单个元素肯定有序,合并只需要根据两个元素的大小进行片段的重排。需要使用额外的内存空间保存两个子片段,然后在原数组上存放合并之后的片段。
  3. 从最小的片段开始,归并到整个数组有序

由上面的过程,知道归并排序需要使用递归来完成,先分割,分割到最底层了,然后开始合并

因此递归函数可以这样写:

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中自带的lessgreater函数对象。

对排序函数使用函数对象进行元素之间的比较,默认使用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;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值