归并排序

1、核心:有序子列的归并

在这里插入图片描述
两个子列一共有N个元素,则归并的时间复杂度:T(N)=O(N)

/* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
void Merge(ElementType A[], ElementType TmpA[],
int L, int R, int RightEnd)
{
	int LeftEnd = R - 1;		//左边终点位置,假设左右两列挨着
	int len = RightEnd - L + 1;
	int i = L;			//存放结果的数组的初始位置
	while (L<=LeftEnd && R<=RightEnd)
	{
		if (A[L]>A[R])
			TmpA[i++] = A[R++];
		else
			TmpA[i++] = A[L++];
	}

	while (L <= LeftEnd)		//直接复制左边剩下的
		TmpA[i++] = A[L++];

	while (R <= RightEnd)		//直接复制右边剩下的
		TmpA[i++] = A[R++];
	
	for (int i = 0; i < len; i++,RightEnd--)
		A[RightEnd] = TmpA[RightEnd];
}

2、递归算法

分而治之
在这里插入图片描述

void MSort(ElementType A[], ElementType TmpA[], int L, int RightEnd)
{
	if(L<RightEnd)
	{
		int center = (L + RightEnd) / 2;
		MSort(A,TmpA,L,center);
		MSort(A,TmpA,center+1,RightEnd);
		Merge(A,TmpA,L,center+1,RightEnd);
	}
}

T(N) = T(N/2) + T(N/2) + O(N)
推倒得出
T(N) = O(N logN)
统一函数接口

void Merge_Sort(ElementType A[], int N)
{
	ElementType *TmpA = (int *)malloc(N*sizeof(ElementType));
	if (TmpA != NULL)
	{
		MSort(A, TmpA, 0, N - 1);
		free(TmpA);
	}
	else
		cout << "空间不足";
}

问题:为什么在Merge,MSort的变量中声明临时数组
如果只在Merge函数中声明临时数组

void Merge( ElementType A[], int L, int R, int RightEnd)
void MSort( ElementType A[], int L, int RightEnd)

看起来会简洁一点,但有很大的问题。
在这里插入图片描述
递归的调用MSort,每一次合并都需要malloc和free,额外的空间复杂度非常大,将临时数组作为Merge和MSort的参数,只需要在Merge_Sort申请一次空间,额外空间复杂度是O( N )

递归算法完整c++代码

#include<iostream>
using namespace std;
typedef int ElementType;
/* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/
void Merge(ElementType A[], ElementType TmpA[],
int L, int R, int RightEnd)
{
	int LeftEnd = R - 1;
	int len = RightEnd - L + 1;
	int i = L;
	while (L<=LeftEnd && R<=RightEnd)
	{
		if (A[L]>A[R])
			TmpA[i++] = A[R++];
		else
			TmpA[i++] = A[L++];
	}

	while (L <= LeftEnd)
		TmpA[i++] = A[L++];
	while (R <= RightEnd)
		TmpA[i++] = A[R++];
	for (int i = 0; i < len; i++,RightEnd--)
		A[RightEnd] = TmpA[RightEnd];
}

void MSort(ElementType A[], ElementType TmpA[], int L, int RightEnd)
{
	if(L<RightEnd)
	{
		int center = (L + RightEnd) / 2;
		MSort(A,TmpA,L,center);
		MSort(A,TmpA,center+1,RightEnd);
		Merge(A,TmpA,L,center+1,RightEnd);
	}
}

void Merge_Sort(ElementType A[], int N)
{
	ElementType *TmpA = (int *)malloc(N*sizeof(ElementType));
	if (TmpA != NULL)
	{
		MSort(A, TmpA, 0, N - 1);
		free(TmpA);
	}
	else
		cout << "空间不足";
}

int main()
{
	int a[] = { 4, 6, 1, 8, 9, 3, 7, 0 };
	int len = sizeof(a) / sizeof(a[0]);
	Merge_Sort(a, len);
	for (int i = 0; i < len; i++)
		cout << a[i] << " ";
	return 0;
}

3、非递归算法

在这里插入图片描述
子序列长度为1归并,再子序列长度为2归并,子序列长度为4归并……
直到子序列长度大于等于数组长度。

先将前面成组的元素进行合并,定义length为子序列的长度,当i <= N-2*length不满足时,剩下的元素没有两个完整的子序列了,如果剩下的元素大于一个子序列,再合并一次,如果小于一个子序列,因为是已经排好序的,直接拷贝过来。这样就完成了子序列长度为length的归并。

void Merge_pass(ElementType A[], ElementType TmpA[],
 int N,int length)		 //length = 当前有序子列的长度
{
	int i;
	//先将前面成组的元素进行合并,
	//当i <= N-2*length不满足时,剩下的元素没有两组了
	for (i = 0; i <= N-2*length; i += 2 * length)
		Merge(A, TmpA, i, i + length, i + 2 * length-1);
	//这里Merge中A元素归并后放入TmpA后,不需要拷贝会数组A了
	if (i + length < N)	//归并最后2个子列
		Merge(A, TmpA, i, i + length, N-1);
	else 			 //最后只剩1个子列
		for (int j = i; j < N; j++)
			TmpA[j] = A[j];
}

子序列长度从1开始,直到大于等于整个数组长度为止。

将A元素归并至TmpA,再将TmpA元素归并至A,每归并一次,子序列长度乘以2。如果某一次将A中元素归并至TmpA中,已经排好序了,子序列长度乘以2,再一次归并是将TmpA中元素拷贝至A中。这样额外的空间复杂度为O(n)。

void Merge_Sort(ElementType A[], int N)
{
	int length = 1; //初始化子序列长度
	ElementType *TmpA;
	TmpA=(int *)malloc(N * sizeof(ElementType));
	if (TmpA != NULL)
	{
		while (length < N)
		{
			Merge_pass(A, TmpA, N, length);
			length *= 2;
			//如果已经排好序了,这一步仅仅是从TmpA中将元素拷贝进A中
			Merge_pass(TmpA, A, N, length);
			length *= 2;
		}
		free(TmpA);
	}
	else
		cout << "空间不足";
}

非递归算法完整c++代码

#include<iostream>
using namespace std;
typedef int ElementType;

//L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置
void Merge(ElementType A[], ElementType TmpA[],
 int L, int R, int RightEnd)
{
	int LeftEnd = R - 1;
	int len = RightEnd - L + 1;
	int i = L;			//最左侧元素下标为L,从L开始归并
	while (L <= LeftEnd && R <= RightEnd)
	{
		if (A[L]>A[R])
			TmpA[i++] = A[R++];
		else
			TmpA[i++] = A[L++];
	}

	while (L <= LeftEnd)
		TmpA[i++] = A[L++];

	while (R <= RightEnd)
		TmpA[i++] = A[R++];
}

void Merge_pass(ElementType A[], ElementType TmpA[],
 int N,int length)//length = 当前有序子列的长度
{
	int i;
	//先将前面成组的元素进行合并,
	//当i <= N-2*length不满足时,剩下的元素没有两组了
	for (i = 0; i <= N-2*length; i += 2 * length)
		Merge(A, TmpA, i, i + length, i + 2 * length-1);
	//这里Merge中A元素归并后放入TmpA后,不需要拷贝会数组A了

	if (i + length < N)				//归并最后2个子列
		Merge(A, TmpA, i, i + length, N-1);
	else						//最后只剩1个子列
		for (int j = i; j < N; j++)
			TmpA[j] = A[j];
}

void Merge_Sort(ElementType A[], int N)
{
	int length = 1; //初始化子序列长度
	ElementType *TmpA;
	TmpA=(int *)malloc(N * sizeof(ElementType));
	if (TmpA != NULL)
	{
		while (length < N)
		{
			Merge_pass(A, TmpA, N, length);
			length *= 2;
			//如果已经排好序了,这一步仅仅是从TmpA中将元素拷贝进A中
			Merge_pass(TmpA, A, N, length);
			length *= 2;
		}
		free(TmpA);
	}
	else
		cout << "空间不足";
}

int main()
{
	int a[] = { 4, 6, 1, 8, 9, 3, 7, 0 };
	int len = sizeof(a) / sizeof(a[0]);
	Merge_Sort(a, len);
	for (int i = 0; i < len; i++)
		cout << a[i] << " ";
	return 0;
}

说明:归并排序占用额外O(n)的空间复杂度,一般不用于内排序,在外排序上有很多优点。

内排序:所有的数据都在内存中;
外部排序:大文件的排序,即待排序的记录存储在外存储器上,无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值