简单易懂排序算法(六)【归并排序】

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/86036575


“归并”一词中文含义就是合并、并入的意思,而在数据结构中的定义是将两个以上的有序表组组合成一个有序表组

归并排序就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后进行两两归并,得到[ n 2 \frac {n}{2} ]个长度为2或1的有序子序列;再两两归并,…,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序

1. 递归版本

根据以上思想,写出如下代码,对每一段代码进行一个简单的分析:

void MergeSort(vector<int>& vec)
{
	MSort(vec, vec, 0, vec.size() - 1);
}

该函数仅仅调用另一个函数 M S o r t MSort 来进行归并排序

//TR2定义为全局变量
#define MAXSIZE 9
vector<int> TR2(MAXSIZE, 0);

void MSort(vector<int> SR, vector<int>& TR1, int start, int end)
{
	int m;
	if (start == end)   //单个序列直接赋值到目标序列中
		TR1[start] = SR[start];
	else
	{
		m = (start + end)/2;  //将SR[s..t]平分为SR[sstart..m]、SR[m+1..end]
		MSort(SR, TR2, start, m); //递归将SR[start..m]归并为有序的TR2[start..m]
		MSort(SR, TR2, m + 1, end);//递归将SR[m+1..end]归并为有序的TR2[m+1..end]
		Merge(TR2, TR1,start, m, end);//将TR2[start..m]、TR2[m+1..end]归并到TR1中
	}	
}

该步关键在于对于归并思想的理解,理解之后将其应用,这里实现了递归版本,在之后将介绍非递归版本;最后调用 M e r g e Merge 函数将两个有序的子序列归并到目标序列中

void Merge(vector<int> SR, vector<int>& TR, int i, int m, int n)
{
	int j,k,l;
	for (j = m + 1,k = i;i <= m && j <= n; ++k)
	{
		if (SR[i] < SR[j])  //判断两个子序列中对应位置元素哪个更大,将大的加入到目标序列TR中
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	//若前序列有余,则加入到目标序列尾后
	if (i <= m)
	{
		for (l = 0; l <= m - i; ++l)
			TR[k] = SR[i]; //将剩余的SR[i..m]复制到TR
	}
	//若后序列有余,则加入到目标序列尾后
	if (j <= n)
	{
		for (l = 0;l <= n - j; ++l)
			TR[k] = SR[j];   //将剩余的SR[j..n]复制到TR
	}
}

M e r g e Merge 函数实现了将两个有序的子序列合并成一个大的有序序列,实现了完整的归并排序

进行一个简单的测试
测试序列:50,10,90,30,70,40,80,60,20
在这里插入图片描述

我在测试代码代码中做了一些处理,使其能观测到调用 M e r g e Merge 函数时的 T R 2 [ s t a r t . . m ] TR2[start..m] T R 2 [ m + 1.. e n d ] TR2[m+1..end] 的变化,对于要合并的两个序列的变化能观察的更为清楚;从结果来看,符合归并排序的思想也完成了排序

测试代码

#include <iostream>
#include <vector>
using namespace std;

#define MAXSIZE 9
vector<int> TR2(MAXSIZE, 0);
int count = 0;

void MergeSort(vector<int>& vec);
void MSort(vector<int> SR, vector<int>& TR1, int start, int end);
void Merge(vector<int> SR, vector<int>& TR,int i, int m, int n);
void PrintResult(vector<int> vec, int n);

void MergeSort(vector<int>& vec)
{
	cout << "--------------归并排序--------------" << endl;
	MSort(vec, vec, 0, vec.size() - 1);
}

void MSort(vector<int> SR, vector<int>& TR1, int start, int end)
{
	int m;
	if (start == end)
		TR1[start] = SR[start];
	else
	{
		m = (start + end)/2;
		MSort(SR, TR2, start, m);
		MSort(SR, TR2, m + 1, end);
		Merge(TR2, TR1,start, m, end);	
	}	
}

void Merge(vector<int> SR, vector<int>& TR, int i, int m, int n)
{
	++count;
	cout << "第" << count << "次:"<< endl; 
	cout << "当前合并序列TR2[start..m]为:";
	PrintResult(SR, SR.size()/2 + 1);
	cout << "当前合并序列TR2[m+1..end]为:";
	for (int x = SR.size()/2 + 1; x <= n; ++x)
	{
		cout << SR[x] << " ";
	}
	cout << endl;
	int j,k,l;
	for (j = m + 1,k = i;i <= m && j <= n; ++k)
	{
		if (SR[i] < SR[j])
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	
	if (i <= m)
	{
		for (l = 0; l <= m - i; ++l)
			TR[k+l] = SR[i+l];
	}
	
	if (j <= n)
	{
		for (l = 0;l <= n - j; ++l)
			TR[k+l] = SR[j+l];   
	}
}

void PrintResult(vector<int> vec, int n)
{
	for (int j = 0; j < n; ++j)
		cout << vec[j] << ' ';
	cout << endl;
}
int main(int argc, char **argv)
{
	int a[] = {50,10,90,30,70,40,80,60,20};
	vector<int> vec(a, a+9);
	MergeSort(vec);
	cout << endl << "最终排序结果为:";
	PrintResult(vec, vec.size());
	return 0;
} 

1.1 复杂度分析

  • 从代码来看,一趟归并需要将 S R [ 1 ] SR[1] ~ S R [ n ] SR[n] 中相邻的长度为h的有序序列进行两两归并。并将结果放在 T R [ 1 ] TR[1] ~ T R 1 [ n ] TR1[n] 中,这需要将待排序序列中的所有记录扫描一遍,因此耗费 O ( n ) O(n) 时间,而由完全二叉树的深度可知,整个归并排序需要[ log 2 n \log_2 {n} ]次,因此,总的时间复杂度为O(n log n \log{n} )
  • 归并排序在排序过程中需要与原始记录序列同样数量的存储空间存放归并结果及递归时深度为 log 2 n \log_2 {n} 的栈空间,因此空间复杂度为O(n+ log n \log{n} )
  • 从代码可以看出只需要两两比较,不需要进行跳跃,因此是一种稳定的排序算法

2. 非递归版本

相比于递归版本,其非递归版本实现更好理解,废话不多说,直接看代码吧:

void MergeSort(vector<int>& vec)
{
	int k = 1;  //k为归并间隔,依次分别为1,2,4,8,16 
	while (k < vec.size() - 1)
	{
		MergePass(vec, TR, k, vec.size() - 1);
		k *= 2;   //间隔倍增
		MergePass(TR, vec, k, vec.size() - 1);  //注意这里需要换回去,因为下一次仍是从vec进行操作
		k *= 2;
	}
}
//s代表此次归并的间隔 
void MergePass(vector<int> SR, vector<int>& TR, int s, int n)
{
	int i = 1;
	int j;
	//i + 2*s -1 <= n 
	while (i <= n - 2*s +1)
	{
		Merge(SR, TR, i, i + s - 1,i + 2*s - 1); //两两进行归并 
		i += 2*s; 
	}
	//归并最后两个序列 
	if (i < n - s +1)
		Merge(SR, TR, i, i +s -1, n);
	//归并最后一个单独序列 
	else {
		for (j = i;j <= n; ++j)
			TR[j] = SR[j];	
	}
}

void Merge(vector<int> SR, vector<int>& TR, int i, int m, int n)
{
	int j,k,l;
	for (j = m + 1,k = i;i <= m && j <= n; ++k)
	{
		if (SR[i] < SR[j])
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	
	if (i <= m)
	{
		for (l = 0; l <= m - i; ++l)
			TR[k+l] = SR[i+l]; //将剩余的SR[i..m]复制到TR
	}
	
	if (j <= n)
	{
		for (l = 0;l <= n - j; ++l)
			TR[k+l] = SR[j+l];  //将剩余的SR[j..n]复制到TR
	}
}

从代码的实现思想来看比较简单,就先将序列两两排序,然后将间隔倍增,将已经两两归并的有序序列再次归并回数组 v e c vec ,然后继续循环,间隔依次倍增:1、2、4、8、16

测试序列:0,50,10,90,30,70,40,80,60,20

此处加0是为了间隔更好的倍增

在这里插入图片描述

2.1 复杂度分析

非递归的迭代方法,避免了递归时深度为 log 2 n \log_2 {n} 的栈空间,空间只是用到申请归并临时用的 T R TR 数组,因此空间复杂度为O(n),并且避免递归也在时间性能上有一定的提升,应该说,使用归并排序时,尽量考虑用非递归方法

测试代码

#include <iostream>
#include <vector>
using namespace std;

#define MAXSIZE 10
vector<int> TR(MAXSIZE, 0);
int count = 0;

void MergeSort(vector<int>& vec);
void MergePass(vector<int> SR, vector<int>& TR, int s, int n); 
void Merge(vector<int> SR, vector<int>& TR,int i, int m, int n);
void PrintResult(vector<int> vec, int n);

void MergeSort(vector<int>& vec)
{
	cout << "--------------归并排序--------------" << endl;
	int k = 1;  //k为归并间隔,依次分别为1,2,4,8,16 
	while (k < vec.size() - 1)
	{
		MergePass(vec, TR, k, vec.size() - 1);
		k *= 2;
		MergePass(TR, vec, k, vec.size() - 1);
		k *= 2;
	}
}
//s代表此次归并的间隔 
void MergePass(vector<int> SR, vector<int>& TR, int s, int n)
{
	int i = 1;
	int j;
	//i + 2*s -1 <= n 
	while (i <= n - 2*s +1)
	{
		Merge(SR, TR, i, i + s - 1,i + 2*s - 1); //两两进行归并 
		i += 2*s; 
	}
	//归并最后两个序列 
	if (i < n - s +1)
		Merge(SR, TR, i, i +s -1, n);
	//归并最后一个单独序列 
	else {
		for (j = i;j <= n; ++j)
			TR[j] = SR[j];	
	}
	cout << "归并间隔为" << s << "得到的序列为:";
	PrintResult(TR,n); 
	cout << endl; 
}

void Merge(vector<int> SR, vector<int>& TR, int i, int m, int n)
{
	int j,k,l;
	for (j = m + 1,k = i;i <= m && j <= n; ++k)
	{
		if (SR[i] < SR[j])
			TR[k] = SR[i++];
		else
			TR[k] = SR[j++];
	}
	
	if (i <= m)
	{
		for (l = 0; l <= m - i; ++l)
			TR[k+l] = SR[i+l];
	}
	
	if (j <= n)
	{
		for (l = 0;l <= n - j; ++l)
			TR[k+l] = SR[j+l]; 
	}
}

void PrintResult(vector<int> vec, int n)
{
	for (int j = 1; j <= n; ++j)
		cout << vec[j] << ' ';
	cout << endl;
}
int main(int argc, char **argv)
{
	int a[] = {0,50,10,90,30,70,40,80,60,20};
	vector<int> vec(a, a+10);
	MergeSort(vec);
	cout << endl << "最终排序结果为:         ";
	PrintResult(vec, vec.size() - 1);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/86036575