归并排序递归和循环实现——学习笔记

归并排序是效率非常高的一种排序方式,和快速排序一样用了分治的思想。分治法的精髓在于将一个复杂问题分割成为多个简单的子问题,然后将子问题逐个解决,最终合并在一起以后就是复杂问题的解了。

归并排序对数组排序时有两种实现方式,一种是利用递归来实现,另一种利用循环来实现,也可以叫自顶向下的归并排序自底向上的归并排序。两种实现方式略有不同,但是基本思想是一样的,那听我慢慢道来。

 

递归版

递归版的归并排序的思想其实挺简单的,简而言之就是将数列不断分割成子序列,直到不能分割为止,然后再将排序好的子序列两两合并最终得到一个完整的数列。具体例子如下:

                       [3   7   6   4   8   9   2   1] 
                           /                  \
分割                 [3  7  6  4]          [8  9  2  1]
                     /          \          /        \
分割              [3   7]   [6   4]      [8   9]   [2   1]
                 /     \    /     \     /     \   /     \
分割            [3]    [7] [6]    [4]  [8]    [9] [2]    [1]
                 \     /   \     /     \     /    \     /
合并              [3   7]   [4   6]      [8   9]   [1   2]
                     \        /             \        /
合并                 [3  4  6  7]          [1  2  8  9]
                          \                     /
合并                   [1   2   3   4   6   7   8   9]

分割时从数列中间开始,将数列分成左右两部分,然后对分割后的序列继续进行分割直到分割不能再分为止。然后对已经排好序的子序列进行合并,重新产生一个排序好的数列。

来看看代码如何实现:

//num -- 待排序的数组
//start -- 排序的起点
//end -- 排序数列的长度
void MergeSort(int *nums, int start, int end) {

	//判断起点是否小于终点
	if (end-start<2) {
		return;
	}

	//进行分割操作
	int mid = (start + end) / 2;
	MergeSort(nums, start, mid);
	MergeSort(nums, mid , end);

	//归并两个子列
	Merge(nums, start, mid, end);
}

//归并函数
void Merge(int *nums, int start, int mid, int end) {
	//需要一个临时数组来存放排序好的数据
	int *temp = (int *)malloc(sizeof(int)*(end - start));

	// 对分割数列进行归并
	int i = start;
	int j = mid;
	int index = 0;

	while (index + start < end) {
		//从两个子序列中取出数据放入临时数组
		if (i < mid && (j == end || nums[i] < nums[j])) {
			temp[index++] = nums[i++];
		}
		else {
			temp[index++] = nums[j++];
		}
	}

	//将临时数组的值重现赋给数组
	for (int i = start; i < end; i++) {
		nums[i] = temp[i - start];
	}

	//释放临时数组
	free(temp);
}

 

循环版 

循环实现和递归版本基本类似,不过理解上可能稍微要难一点点,也会多一点技巧,先看看思路:

                       [3   7   6   4   8   9   2]
                        |   |   |   |   |   |   |
分割                   [3] [7]  [6] [4] [8] [9] [2]
                      /    /    /   /    |   |   \
合并                [3   7]    [4   6]  [8   9]  [2]
                        \        /        \      /
合并                   [3  4  6  7]     [2  8  9]
                             \             /
合并                   [2   3   4   6   7   8   9]

简单来说就是先把数组划分为n组,刚开始每组只有一个值。然后相邻的小组不断进行两两合并,最终合并成为一个组。我这里用了奇数个数据,方便理解对于非2的幂次方时数组的处理逻辑。

看下代码:

//nums -- 待排序数组
//length -- 数组长度
void MergeSort(int *nums, int length) {

	//i每次乘2,是因为每次合并以后小组元素也变成两倍个了
	for (int i = 1; i < length; i *= 2) {

		//对拥有2的幂次方个数值的小组进行两两合并
		int index = 0;
		while (2 * i + index <= length) {
			index += 2 * i;
			Merge(nums, index - 2 * i, index - i, index);
		}

		//index+i<length 说明剩余两个小组,但其中一个小组数据的数量不满足2的幂次个,对两个小组进行合并
		if (index + i <length) {
			Merge(nums, index, index + i, length);
		}
	}
}

void Merge(int *nums, int start, int mid, int end) {
	//需要一个临时数组来存放排序好的数据
	int *temp = (int *)malloc(sizeof(int)*(end - start));

	// 对分割数列进行归并
	int i = start;
	int j = mid;
	int index = 0;

	while (index + start < end) {
		//从两个子序列中取出数据放入临时数组
		if (i < mid && (j == end || nums[i] < nums[j])) {
			temp[index++] = nums[i++];
		}
		else {
			temp[index++] = nums[j++];
		}
	}

	//将临时数组的值重现赋给数组
	for (int i = start; i < end; i++) {
		nums[i] = temp[i - start];
	}

	//释放临时数组
	free(temp);
}

 

上面介绍的数组的归并排序算法,如果想知道如何对链表进行归并排序,点击这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值