排序 “肆” 之归并排序

1. 归并排序

1.1 原理介绍

归并排序的基本原理是将一个未排序的数组分解较小的子数组,然后递归地对这些子数组进行排序,最后再将排好序的子数组合并成一个有序数组。其核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列

其主要步骤包括:

  1. 分割:将原始数组分割为较小的子数组,直到每个子数组只包含一个元素。
  2. 合并:逐个比较两个子数组的元素,并按顺序合并它们。

1.2 主要步骤

详细步骤:

  1. 分解:将待排序数组分解成两个大致相等的子数组,直到子数组中只有一个元素为止。这一步是通过递归实现的。
  2. 合并:将两个已排序的子数组合并成一个有序数组。在合并过程中,创建一个临时数组,比较两个子数组的元素,将较小的元素依次放入临时数组中。
  3. 递归合并:重复步骤 2,直到所有子数组都合并成一个完整的有序数组。

下面是一个示例来说明归并排序的详细步骤:

  • 假设待排序数组为 38, 27, 43, 3, 9, 82, 10
  1. 初始状态:原始数组为 38, 27, 43, 3, 9, 82, 10
  2. 第一次分解:将数组分解为 38, 27, 43, 39, 82, 10 两个子数组。
  3. 对左边子数组进行排序:分解左边子数组 38, 27, 43, 3,分解为 38, 2743, 3;继续分解为 3827,以及 433。 对每个子数组进行合并排序,得到 27, 383, 43。最终合并左边子数组得到 3, 27, 38, 43
  4. 对右边子数组进行排序:分解右边子数组 9, 82, 10,分解为 982, 10。继续分解为 8210。对每个子数组进行合并排序,得到 10, 82。最终合并右边子数组得到 9, 10, 82
  5. 合并左右子数组:将左边子数组 3, 27, 38, 43 和右边子数组 9, 10, 82 合并,依次比较两个子数组的元素,将较小的元素放入临时数组中。最终得到合并后的有序数组 3, 9, 10, 27, 38, 43, 82
  6. 最终结果:经过以上步骤,我们得到了完整的有序数组 3, 9, 10, 27, 38, 43, 82

1.3 代码示例

cpp 复制代码
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	//递归排序左右两部分
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	//合并两个有序数组
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	//复制剩余元素
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	//合并临时数组到原数组
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin) + 1);
}

//归并排序
void MergeSort(int* a, int n)
{
	//创建临时空间
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);

	//手动释放临时空间
	free(tmp);
}

//测试
int main()
{
	int a[] = { 2,4,6,1,5,3,9,7,0,8 };
	int n = sizeof(a) / sizeof(int);

	printf("原数组为:");
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}

	MergeSort(a, n);

	printf("\n排序后数组为:");
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

1.4 特性总结

  1. 稳定性:归并排序是一种稳定的排序算法,相同元素的相对位置在排序前后不会改变。
  2. 时间复杂度:归并排序的时间复杂度为 O(N*log N),其中 N 是待排序数组的长度。
  3. 空间复杂度:归并排序的空间复杂度为 O(N),需要额外的空间来存储临时数组。
  4. 适用性:归并排序适用于各种数据规模的排序,且在处理大规模数据时表现良好。
相关推荐
疯狂打码的少年39 分钟前
【数据结构】二叉排序树(BST)的定义与操作
数据结构·笔记·算法
wabs6661 小时前
关于字符串【力扣541.反转字符串II的思考】
数据结构·算法·leetcode·字符串
旖旎夜光2 小时前
LeetCode 30:串联所有单词的子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
白狐_7983 小时前
408 数据结构|完全二叉树两道典型题:第 6 层叶结点数与叶结点总数
数据结构·算法
郝学胜_神的一滴4 小时前
并查集深度入门:从玄学抽象到 QuickFind & QuickUnion 源码实战
数据结构·算法
GeekZHR4 小时前
C语言指针2:数组名、二级指针、指针数组,一次把“指针和数组“讲透
c语言·数据结构·算法·指针
烬羽5 小时前
求第 K 大,为什么反而要用最小堆?
javascript·数据结构·算法
郝学胜-神的一滴6 小时前
并查集深度入门:从玄学抽象到 QuickFind & QuickUnion 源码实战
数据结构·c++·python·程序人生·算法·软件开发
hold?fish:palm17 小时前
24 回文链表
数据结构·c++·链表
qeen8721 小时前
【数据结构】自平衡二叉搜索树各种旋转算法原理解析及AVL树的C++实现
数据结构·c++·算法