归并(Merge Sort)排序算法

归并排序是一种使用归并技术进行排序的算法,包括自底向上和自顶向下的实现方法。它通过将有序子文件两两合并,最终形成一个完整的有序文件。归并排序具有稳定性,时间复杂度为O(nlogn),但需要O(n)的额外空间。在多链表排序中表现出色。给出的代码展示了归并排序的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

归并排序(Merge sort)是利用"归并"技术来进行排序的。归并是指将若干个已排序的子文件合并成一个有序的文件。

1、归并排序的基本思想

两个归并算法的基本思想:设有两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:A[low...m],A[m+1...high],先将他们合并到一个暂存的向量Temp(相当于 输出堆)中,待合并完成后将Temp复制回A[low...high]中。

归并排序有两种实现方法:自底向上和自顶向下。

自底向上方法的基本思想:

(1) 第1趟归并排序时,将待排序的文件A[1...n]看作n个长度为1的有序子文件,将这些子文件两两归并。若n为偶数,则得到n/2个长度为2的有序子文件;若n为奇数,则 最后一个子文件不参与归并。所以本趟归并完成后,前n/2个有序子文件长度为2,但最后一个子文件长度为1。

(2) 第2趟归并则是将第1趟归并所得到的n/2个有序的子文件两两归并,如此反复,直到最后得到一个长度为n的有序文件为止。

(3) 上述每次归并操作,均是将两个有序的子文件合并成一个有序的子文件,所以称为"二路归并排序"。类似地,有k(k>2)路归并排序。

自顶向下方法的基本思想更为简洁:设归并排序的当前区间是A[low...high],步骤如下:

(1) 分解:将当前的区间一分为而,即求分裂点。

(2) 求解:递归地对两个子区间A[low...mid]和A[mid+1...high]进行归并排序。

(3) 组合:将已排序的两个子区间A[low...mid]和A[mid+1...high]归并为一个有序的区间R[low...high]。

(4) 递归的终止条件:子区间长度为1(一个记录自然有序)。

2、思想

多次将两个或两个以上的有序表合并成一个新的有序表。 

3、算法复杂度

最好的情况下:一趟归并需要n次,总共需要logN次,因此为O(N*logN)

最坏的情况下,接近于平均情况下,为O(N*logN)

说明:对长度为n的文件,需进行logN 趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)

4、稳定性

归并排序最大的特色就是它是一种稳定的排序算法。归并过程中是不会改变元素的相对位置的。

缺点是,它需要O(n)的额外空间。但是很适合于多链表排序。(例如将两个有序链表合并成一个有序的链表)

5、代码实现

//实现归并排序的算法(使用自顶向下的方法)
//归并排序是复杂的排序。
//1.在归并排序之前,先分配一个数组长度大小的临时空间,用于存放数组归并排序的结果.
//2.先将整个数组每次二分,二分的终止条件是低位下标大于等于高位,递归调用直到终止.
//3.调用归并时,传入左侧开始位置、右侧开始位置和右侧结束位置,临时数组开始位置为左侧位置,左侧结束位置为右侧开始位置-1
//4.确定临时数组长度为右末-左初+1.
//5.先从左初位置和右初位置开始遍历,比较两端的元素值,把较小的值放入temp临时数组中,再从中找出次小的值存入数组中,直到左初位置大于左末,或者是右初位置大于右末位置。
//6.不管怎么操作肯定会有一段含有剩余元素,遍历左初到左末,右初到右末位置,把剩余元素放入数组中
//7.把临时数组中的数据拷贝到原始数组中,循环次数为数组长度,从右末位置开始。
//8.循环2-8步,直到原始数组有序为止。
#include<iostream>
using namespace std;

//将分值的两端按大小次序填入临时数组,最后临时数组拷贝到原始数组中
//lPos到rPos-1为一端,rPos到rEnd为另外一端
static void Merge(int a[], int temp[], int lPos, int rPos, int rEnd)
{
	int i, NumElement, lEnd, tempPos;
	tempPos = lPos;
	lEnd = rPos - 1;						//从左端开始
	NumElement = rEnd - lPos + 1;					//数组长度

	while ((lPos <= lEnd) && (rPos <= rEnd))
	{
		if (a[lPos] <= a[rPos])					//比较两端的元素值
			temp[tempPos++] = a[lPos++];			//把较小的值先放入temp临时数组中
		else
			temp[tempPos++] = a[rPos++];
	}

	//到这里,左端大或者右端只能有一端还可能含有剩余元素
	while (lPos <= lEnd)						//把左端剩余的元素放入temp中
		temp[tempPos++] = a[lPos++];
	while (rPos <= rEnd)						//把右端剩余的元素放入temp中
		temp[tempPos++] = a[rPos++];
	for (i = 0; i < NumElement; i++, rEnd--)
		a[rEnd] = temp[rEnd];					//把临时数组拷贝到原始数组中
}

static void msort(int a[], int temp[], int low, int high)
{
	if (low >= high)						//结束条件,当low和high中间没有元素return
		return;
	int middle = (low + high) / 2;					//计算分裂点
	msort(a, temp, low, middle);					//对子区间[low,middle]递归做归并排序
	msort(a, temp, middle + 1, high);				//对子区间[middle+1,high]递归做归并排序
	Merge(a, temp, low, middle + 1, high);			<span style="white-space:pre">	</span>//组合,把两个有序区合并为一个有序区
}
static void merge_sort(int a[], int len)
{
	int* temp = NULL;						//分配临时数组空间
	temp = new int[len];
	if (temp != NULL)
	{
		msort(a, temp, 0, len - 1);				//调用msort归并排序
		delete[] temp;						//释放临时数组内存
	}
}

static void print_array(int a[], int length)
{
	for (int i = 0; i < length; i++)				//打印元素
	{
		cout << a[i] << " ";
	}
}
void main9mianshiti7()
{
	int a[] = { 8, 6, 1, 3, 5, 2, 7, 4 };
	cout << "before merge sort:";
	print_array(a, 8);
	merge_sort(a, 8);						//归并排序
	cout << "\nafter merge sort:";
	print_array(a, 8);
	system("pause");
}
6、测试结果

before merge sort::8 6 1 3 5 2 7 4

after merge sort:1 2 3 4 5 6 7 8 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值