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

1. 归并排序

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

基本步骤:

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

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

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

void MergeSort(vector& nums, int start, int end, vector& 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& nums, int start, int mid, int end, vector& 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 
#include 
using namespace std;

void Sort(vector& nums);
void MergeSort(vector& nums, int start, int end, vector& tmp);
void Merge(vector& nums, int start, int mid, int end, vector& tmp);
void printArray(vector& nums);

int main()
{
	vector 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& nums) {
	vector tmp(nums.size());
	MergeSort(nums, 0, nums.size() - 1, tmp);
}
//归并排序的入口
void MergeSort(vector& nums, int start, int end, vector& 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& nums, int start, int mid, int end, vector& 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& nums) {
	for (auto it = nums.begin(); it != nums.end();++it)cout << *it << " ";
	cout << endl;
}


2. 使用函数模板和函数对象实现通用的归并排序算法

使用函数模板可以将上面的针对int类型的归并排序拓展到不同的数据类型,使用函数对象可以将自定义的比较函数传入归并排序的算法中,实现对自定义数据类型的排序。对于基本数据类型,可以使用STL中自带的lessgreater函数对象。

对排序函数进行重载,对基本数据类型,不传入比较函数的情况下,默认使用STL的less,即实现升序排序。

完整代码如下:

// MergeSort_Vector_Template.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include 
#include 
#include 
using namespace std;

template	//通用接口
void Sort(vector& nums, _compare cmp );

template						//通用接口,重载,默认使用less作为比较函数
void Sort(vector& nums);

template		//归并排序的入口
void MergeSort(vector& nums, int start, int end, vector& tmp, _compare cmp);

template		//实现具体归并的函数
void Merge(vector& nums, int start, int mid, int end, vector& tmp, _compare cmp);

template						//vector的打印函数
void printArray(vector& nums);


//自定义仿函数,作为比较函数
template
struct cmp {
	bool operator()(T& a, T& b) {
		return a < b;
	}
};

int main()
{
	vector a({ 5,6,1,8,3,4,9,7,2,3 });
	vector b({"hello","world","hahaha", "welcome", "goodbye","nice","apple" });
	//打印a和b的初始值
	cout << "Initial value of vecotr a:";
	printArray(a);
	
	cout<< "Initial value of vecotr b:";
	printArray(b);

	//使用STL中的比较函数,less:升序排列;greator:降序排列
	Sort(a,less());
	//使用自定义的比较函数,可以支持自定义数据类型(或者针对自定义数据类型,重载"<"或">"运算符,可以使用less或者greater
	Sort(b, cmp());
	//打印排序后的a和b
	cout << "Sorted value of vecotr a:";
	printArray(a);
	cout << "Sorted value of vecotr b:";
	printArray(b);

	return 0;
}

//通用接口
template
void Sort(vector& nums, _compare cmp) {

	vector tmp(nums.size());
	MergeSort(nums, 0, nums.size() - 1, tmp, cmp);
}
//重载的带默认比较函数的排序
template
void Sort(vector& nums) {
	vector tmp(nums.size());
	MergeSort(nums, 0, nums.size() - 1, tmp, less());
}

//归并排序的入口
template
void MergeSort(vector& nums, int start, int end, vector& tmp, _compare cmp) {
	if (nums.empty() || start >= end)return;

	int mid = start + (end - start) / 2;
	//将待排序区间从中间分割开
	MergeSort(nums, start, mid, tmp, cmp);
	MergeSort(nums, mid + 1, end, tmp, cmp);
	//将分割开的元素进行合并
	Merge(nums, start, mid, end, tmp, cmp);
}
/**实现具体归并的函数
*
*该函数将两个有序的数组进行合并,合并为一个有序的数组
*传入参数:第一个已排序序列[start, mid];第二个已排序序列[mid+1, end];临时存储数组tmp
*/
template
void Merge(vector& nums, int start, int mid, int end, vector& 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的打印函数
template
void printArray(vector& nums) {
	for (auto it = nums.begin(); it != nums.end(); ++it)cout << *it << " ";
	cout << endl;
}

 

你可能感兴趣的:(C++,数据结构与算法,排序算法,c++)