C/C++ 七大排序算法 之 “归并排序”

排序:即将一组混乱的数据按从小到大或者从大到小的顺序进行有序的排列出来。

归并排序算法图解
在这里插入图片描述
图来自网络

思路解答
归并排序简单的理解就是将两组有序的数组进行排序;如下图
在这里插入图片描述
用两个数组段开头的元素进行比较,找出最小的值存储到新的数组中,以此类推,知道有序。

以上,我们写一个小例子感受一下归并排序:

// 参数一:数组  参数二:数组排序起始索引  参数三:数组个数中间值  参数四:数组排序结束索引
void mergeAdd_demo(int arr[], int left, int mid, int right) {
	int tmp[64] = { 0 };
	int i = left;	// 指向左边数组最小的元素位置
	int j = mid;	// 指向右边数组最小的元素位置
	int index = 0;	// tmp的索引下标


	// 当两个数组块中都还存在“值”
	while (i < mid && j <= right) {	
		// 比较,得知最小值下标
		if (arr[i] < arr[j]) {
			tmp[index] = arr[i];	// 将最小值赋值给临时数组
			index++;
			i++;

		} else {
			tmp[index] = arr[j];
			index++;
			j++;
		}
	}

	// 处理数组块中剩余得数据
	while (i < mid) {
		tmp[index] = arr[i];
		index++;
		i++;
	}

	while (j <= right) {
		tmp[index] = arr[j];
		index++;
		j++;
	}

	// 内存拷贝
	memcpy(arr + left, tmp, sizeof(int) * (right - left + 1));

}

int main(void) {
	int arrays[] = { 1, 3, 6, 7, 2, 4, 5, 8 };
	int len = sizeof(arrays) / sizeof(arrays[0]);
	int *tmp = new int[len];

	// 取中间值
	int mid = len / 2;

	mergeAdd_demo(arrays, 0, mid, len - 1);


	for (int x : arrays) {
		printf("%d, ", x);
	}

	return 0;
}

运行截图:
在这里插入图片描述

int arrays[] = { 1, 3, 6, 7, 2, 4, 5, 8 };
上面数组,共8个元素,数组数据对半分的话,是两段已经有序的的序列,因此符合使用归并排序,进行排序。
根据参数,传的是数组起始索引、数据中间值索引、数组末尾索引。
while (i < mid && j <= right)循环就是使用这些参数进行两段数据的判断取最小值排序。

其实如果两段数据是有序的,归并排序就是这么简单,但是事实却非如此;我们得到的数据往往是无序的,这时候,就得想办法去将数据变法两段有序的数据,然后就可以使用归并排序了。

这时,就得稍微改一下代码,使用递归方式才可以得到递归的效果。

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

// 归并排序
void mergeAdd(int arr[], int left, int mid, int right, int *tmp);	// 参数一:数组  参数二:数组排序起始索引  参数三:数组个数中间值  参数四:数组排序结束索引  参数五:临时排序数组

// 递归执行归并排序
void mergeSort(int arr[], int left, int right, int *tmp);	// 参数一:数组  参数二:排序起始索引  参数三:排序最后索引  参数四:临时排序数组


int main(void) {
	int arrays[] = { 8, 4, 5, 7, 1, 3, 6, 2 };
	int len = sizeof(arrays) / sizeof(arrays[0]);
	int *tmp = new int[len];

	// 取中间值
	int mid = len / 2;

	mergeSort(arrays, 0, len - 1, tmp);

	for (int x : arrays) {
		printf("%d, ", x);
	}

	return 0;
}



// 归并排序大法
void mergeAdd(int arr[], int left, int mid, int right, int *tmp) {
	//int tmp[64] = { 0 };
	int i = left;	// 指向左边数组最小的元素位置
	int j = mid;	// 指向右边数组最小的元素位置
	int index = left;	// tmp的索引下标


	// 当两个数组块中都还存在“值”
	while (i < mid && j <= right) {
		// 比较,得知最小值下标
		if (arr[i] < arr[j]) {
			tmp[index] = arr[i];	// 将最小值赋值给临时数组
			index++;
			i++;

		} else {
			tmp[index] = arr[j];
			index++;
			j++;
		}
	}

	// 处理数组块中剩余的数据
	while (i < mid) {
		tmp[index] = arr[i];
		index++;
		i++;
	}

	while (j <= right) {
		tmp[index] = arr[j];
		index++;
		j++;
	}

	// 内存拷贝
	memcpy(arr + left, tmp + left, sizeof(int) * (right - left + 1));

}


// 归并排序递归实现
void mergeSort(int arr[], int left, int right, int *tmp) {
	int mid = 0;

	if (left < right) {
		mid = left + (right - left) / 2;	// 进行对半分治
		printf("left = %d  right = %d\n", left, right);
		mergeSort(arr, left, mid, tmp);
		mergeSort(arr, mid + 1, right, tmp);
		mergeAdd(arr, left, mid + 1, right, tmp);
	}
}

运行截图:
在这里插入图片描述

递归效果如下图:
在这里插入图片描述
图来自网络

其实思想都是一样,8个数据对半分成4、4数据,然后4在进行对半分成2、2数据,然后2再进行对半分成1、1数据,就可以开始排序了。

思想不难,主要是递归实现就太难啦!
以上图为例,经过我调试,发现其像二叉树一样,首先处理左子节点,然后再处理右子节点。

文字详细解释
数据:8,4,5,7,1,3,6,2
也就是先将数据分成8,4,5,7和1,3,6,2
然后先处理左边数据,继续分成8,4和5,7
然后再处理左边数据,继续分成8和4
然后可以将8和4进行排序,排序完毕
然后再处理右边数据,继续分成5和7
然后可以将5和7进行排序,排序完毕
然后再将左边排好序的数据排序4,8和5,7
排序完毕,得出左边数据为4,5,7,8

然后再处理右边数据,继续分成1,3和6,2
然后再处理左边数据,继续分成1和3
然后可以将1和3进行排序,排序完毕
然后在处理右边数据,继续分成6和2
然后可以将6和2进行排序,排序完毕
然后再将右边排序序的数据排序1,3和2,6
排序完毕,得出右边数据为1,2,3,6

最后将左右两边已经排号序的数据进行排序
即将4,5,7,8和1,2,3,6进行排序
最后得出排序结果1,2,3,4,5,6,7,8
排序完毕!

总结

归并排序其实不难,只是递归那里比较难理解,他的执行顺序是怎么样的?
可以根据上图运行结果打印出来的索引进行理解,也可以根据分治图进行理解,最后再看我的文字解释,相信一定会理解的!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cpp_learners

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

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

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

打赏作者

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

抵扣说明:

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

余额充值