排序 “肆” 之归并排序

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, 3 和 9, 82, 10 两个子数组。
  3. 对左边子数组进行排序:分解左边子数组 38, 27, 43, 3,分解为 38, 27 和 43, 3;继续分解为 38 和 27,以及 43 和 3。 对每个子数组进行合并排序,得到 27, 38 和 3, 43。最终合并左边子数组得到 3, 27, 38, 43。
  4. 对右边子数组进行排序:分解右边子数组 9, 82, 10,分解为 9 和 82, 10。继续分解为 82 和 10。对每个子数组进行合并排序,得到 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. 适用性:归并排序适用于各种数据规模的排序,且在处理大规模数据时表现良好。
相关推荐
jeo陽1 小时前
05-数组与冒泡排序:第一个数据结构与经典算法
c语言·数据结构·算法
Nil2081 小时前
leetcode 75颜色分类
数据结构·算法·leetcode
小poop2 小时前
高级排序算法与性能对比——归并、非比较排序及总结
数据结构·算法·排序算法
用户84913717547163 小时前
Redis 跳表用了"随机"的魔法,实现了比平衡树更快的查找!
数据结构·redis
不会就选b3 小时前
算法日常・每日刷题--<动态规划>29
数据结构·算法·leetcode
垆边人似月.3 小时前
最少的元素个数使和为 target
数据结构·c++·算法
彧azz4 小时前
B-树学习总结:从结构特性到核心操作的系统复习
数据结构·笔记·b树·学习
All for pursuit.14 小时前
【动态规划-21】312.戳气球
数据结构·c++·算法·leetcode·动态规划
YSL07012418 小时前
贪吃蛇小游戏---古法编程
数据结构·游戏程序
y1su18 小时前
Leetcode 动态规划 模板
java·数据结构·算法·leetcode·动态规划