【算法】归并排序

归并排序的核心:两个有序子列的归并

(图片来自 中国大学MOOC《数据结构》)

如上所示,假如我们已经有了这样两个有序的子序列,首先要做的就是把这两个子列合并。为此,可以开另外一个数组,然后把这几个数字按照一定次序放进去,保证最后那个数组是从小到大有序的。

如上,我们分别用三个指针指向这几个数组的开始(注意:这里的指针不是C语言里明确定义的那个指针,而是我们借用它指向了一个地址,其不一定为指针,例如,数组下标)

接下来要做的就很简单了,比较Aptr 和 Bptr所指值的大小,1<2,所以把1放入Cptr所指的位置,然后Aptr和Cptr均向右移一位,如下图所示:

剩余的事情是重复的,就不再讲了,现在,就用具体的函数将其实现:

void merge(int elem[], int tmp[], int left, int right, int right_end)  //归并函数,只负责归并两个有序子列
{    //elem为原始数据,包含两个子序列。left为子序列1的起点,right_end为子序列2的终点
	int i = 0;
	int left_end = right - 1;  //子序列1的最右端
	int t = left;              //临时数组的起点	
	int nums = right_end - left + 1; //总元素个数

	while (left <= left_end && right <= right_end)
	{ //仅当两个子序列都有元素,进入循环
		if (elem[left] <= elem[right])
			tmp[t++] = elem[left++];
		else
			tmp[t++] = elem[right++];
	} //此循环退出后,必有一个序列还有剩余元素
	while (left <= left_end) //若序列1有剩余,直接依次放入临时数组
		tmp[t++] = elem[left++];
	while (right <= right_end) //若序列2有=剩余,直接依次放入临时数组,这两个while循环只有一个会进入循环体执行
		tmp[t++] = elem[right++];
	for (i = 0; i < nums; i++, right_end--) //再把tmp的元素倒回到原始数组
		elem[right_end] = tmp[right_end];
}

然后具体实现这个归并排序的递归算法:

void m_sort(int elem[], int tmp[], int left, int right_end)
{
	int center;
	if (left < right_end) {  //当满足数组有元素的时候进入,当元素个数为1时(left==right_end),则返回,因为只有一个元素,必定有序
		center = (right_end - left) / 2 + left;   //求取中心元素的位置
		m_sort(elem, tmp, left, center);          //调用自身对左半边进行排序
		m_sort(elem, tmp, center + 1, right_end); //对右半边进行排序
		merge(elem, tmp, left, center + 1, right_end);  //将两个有序子列归并到一块,调用了我们上边实现的函数
	}
}
现在,核心的算法已经实现了。但是,这个函数每次调用的时候都要=传一大堆的参数进去,是很不友好的。所以我们可以给它写一个壳,提供一个统一的接口:

void merge_sort(int elem, int n)  //只留下两个参数,待排数组和元素个数
{
	int *tmp = (int *)malloc(n * sizeof(int));
	if (tmp) {
		m_sort(elem, tmp, 0, n - 1);
		free(tmp);
	}
	else
		printf("空间不足!\n");
}
至此,整个算法就实现完成了!

最后附上我测试用的代码:

#include <stdio.h>
#include <stdlib.h>

#define N 1000

int main(void)
{
	int arr[N] = {0};
	int i = 0, j = 1000;

	for (i = 0; i < N; i++) {
		arr[i] = j--;
	}
	merge_sort(arr, N);
	for (i = 0; i < N; i++) {
		printf("%d ", arr[i]);
	}

	system("pause");
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fireplusplus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值