[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序

这篇具有很好参考价值的文章主要介绍了[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、归并的思想

2、归并排序的思想

2.1 基本思想

2.2 图解分析

3、归并排序递归版本代码实现

3.1 代码解析

3.2 注意事项

3.2.1错误划分:[begin, mid-1],[mid, end]

3.2.2 正确划分:[begin, mid], [mid+1, end]

4、归并排序的测试

5、时间复杂度、空间复杂度分析

5.1 时间复杂度

5.2 空间复杂度


1、归并的思想

这是我们第二次了解归并的思想了,第一次在我们之前的链表oj题里面,合并两个有序链表,我们当时解题的思想就是归并的思想。

我们这次来系统的学习一下归并的思想(本篇以升序为例展开):

归并两个数组(链表)时,我们使用两个指针指向不同的数组首元素,控制并遍历两个数组,比较两个指针指向的值,小的我们放入开辟的临时数组里面,然后让指向小值的指针往后走一步,继续比较。不断去比,总有一个数组先被遍历完,由于是有序数组,另外那个没有被遍历完的数组直接连接到临时数组的后面就完成了排序。

我们画图来理解这个思想:

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

2、归并排序的思想

2.1 基本思想

归并排序是建立在归并操作上的一种有效的排序算法。

1.归并排序的思想是,将一个数组二分为左右两个数组,然后对左右两个数组继续进行二分,直到分为的子区间为一个数据的时候,这一个数据一定是有序的,便不再二分。

2.接下来就对左右两个子区间进行排序合并,不断排序合并,最终就实现了整个数组的排序。

2.2 图解分析

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

我们归并的时候不是直接在原数组上就交换的,直接在原数组上操作会产生覆盖,导致错误。因此我们是malloc一个与原数组大小相同的空间tmp,将每次合并后的值拷贝到tmp中,合并一次拷贝一次,最中tmp数组为有序,再将tmp使用memcpy拷贝回原数组,再free(tmp) (防止内存泄漏),整个排序就完成了。

3、归并排序递归版本代码实现

// 归并排序递归实现
// 时间复杂度:O(N*logN)
// 空间复杂度:O(N + logN)
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	//[begin, mid] [mid+1, end]
	_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 (NULL == tmp)
	{
		perror("malloc fail:");
		return;
	}
	_MergeSort(a, 0, n-1, tmp);
	free(tmp);
}
 

3.1 代码解析

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

我们在这一段使用了递归,递归的思想就是二分,将数组分为左右两个区间,然后再对左右两个区间继续进行二分,当 begin >= end 时,就是最小的区间,然后我们就进行排序+合并。

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

这一段就是排序+合并,我们对两个子区间进行排序+合并,思路就是我们归并的思想。这里要注意,我们tmp的下标是begin,因为我们需要将每一层合并的值先放进tmp中,然后再拷贝到原数组里,如果是0,拷贝回去的时候就会出现错误。

3.2 注意事项

在划分左右区间的时候一定是 [begin, mid],[mid+1, end],

不能是 [begin, mid-1],[mid, end]!!!

这里很容易出错,看着逻辑上没问题,但是在排序的时候就会出问题。

3.2.1错误划分:[begin, mid-1],[mid, end]

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

3.2.2 正确划分:[begin, mid], [mid+1, end]

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

总结:造成第一种划分死循环的原因是,当我们 mid = begin+end/2 时,计算器是向下取整的,所以一旦向下取整的时候,我们的区间就要包含mid这个值,这样就会补上向下取整造成的舍去。

4、归并排序的测试

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end]
	_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 (NULL == tmp)
	{
		perror("malloc fail:");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);

	free(tmp);
}
void test()
{
	int a[] = { 6,3,2,1,5,7,9 };
	Print(&a, sizeof(a) / sizeof(int));
	MergeSort(&a, sizeof(a) / sizeof(int) - 1);
	Print(&a, sizeof(a) / sizeof(int));
}
int main()
{
	test();

	return 0;
}
 

测试结果:

[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序,排序算法,排序算法,算法,数据结构,c语言

5、时间复杂度、空间复杂度分析

5.1 时间复杂度

归并排序区间不断二分,时间复杂度为O(logN);然后再合并,时间复杂度为O(N)。

整体的时间复杂度为o(N*logN)。

5.2 空间复杂度

因为我们开辟了一个临时空间用于排序,因此空间复杂度O(N)。文章来源地址https://www.toymoban.com/news/detail-573259.html

到了这里,关于[数据结构 -- 手撕排序算法第七篇] 递归实现归并排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法篇】手撕八大排序算法之快排的非递归实现及递归版本优化(三路划分)

    ​👻内容专栏: 《数据结构与算法篇》 🐨本文概括: 利用数据结构栈(Stack)来模拟递归,实现快排的非递归版本;递归版本测试OJ题时,有大量重复元素样例不能通过,导致性能下降,优化快速排序通过将数组划分为三个区域,可以更有效地处理重复元素。 🐼本文作者:

    2024年02月11日
    浏览(83)
  • [数据结构 -- 手撕排序算法第六篇] 递归实现快速排序(集霍尔版本,挖坑法,前后指针法为一篇的实现方法,很能打)

    目录 1、常见的排序算法 1.1 交换排序基本思想 2、快速排序的实现方法 2.1 基本思想 3 hoare(霍尔)版本 3.1 实现思路 3.2 思路图解 3.3 为什么实现思路的步骤2、3不能交换 3.4 hoare版本代码实现 3.5 hoare版本代码测试 4、挖坑法 4.1 实现思路 4.2 思路图解 4.3 挖坑法代码实现 4.4 挖坑

    2024年02月16日
    浏览(49)
  • 【数据结构】手撕排序

    🔥 博客主页 : 小羊失眠啦. 🎥 系列专栏 : 《C语言》 《数据结构》 《Linux》 《Cpolar》 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 排序 :所谓排序就是使一串记录,按照其中的某个或某些的大小, 递增或递减 的排列起来的操作。排序算法,就是如何使得记录按照要求

    2024年02月05日
    浏览(52)
  • [数据结构 -- 手撕排序第三篇] 冒泡排序

    目录 1、常见的排序算法 1.1 交换排序基本思想 2、冒泡排序的实现 2.1 基本思想 2.2 单趟排序 2.2.1 单趟排序分析 2.2.2 单趟排序实现代码 3、冒泡排序完整代码实现 3.1 思路分析 3.2 代码实现 4、时间复杂度 5、优化算法 5.1 优化算法思路 5.2 优化算法代码实现 6、冒泡排序的特性总

    2024年02月13日
    浏览(45)
  • 【数据结构】手撕排序NO.1----排序初识

    目录  一. 前言 二. 排序的概念及运用         2.1 排序的概念         2.2 排序的运用         2.3 常见的排序算法 三. 冒泡and选择排序         3.1 冒泡排序         3.2 选择排序 四. 各大排序算法的复杂度和稳定性        从本期开始,我们的数据结构将迎来一个新的

    2024年02月16日
    浏览(57)
  • [数据结构 -- 手撕排序第一篇] 插入排序

    目录 1、常见的排序算法 2、插入排序的思路 2.1 基本思想 2.2 直接插入排序 2.2.1 单趟排序的思路 2.2.2 单趟排序代码实现 3、插入排序代码 4、插入排序+打印测试 5、插入排序的时间复杂度 5.1 最坏情况 5.2 最好情况 6、直接插入排序的特性总结   直接插入排序是一种简单的插入

    2024年02月12日
    浏览(55)
  • 【数据结构】手撕归并排序(含非递归)

    目录 一,归并排序(递归) 1,基本思想  2,思路实现 二,归并排序(非递归) 1,思路实现 2,归并排序的特性总结: 1,基本思想 归并排序 (MERGE-SORT) 是建立在 归并操作 上的一种 有效的排序算法 ,该算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用; 将 已

    2024年02月08日
    浏览(47)
  • 【数据结构】手撕排序NO.2----直接插入排序与希尔排序

      目录 一. 导入 二. 直接插入排序         2.1 基本思想         2.2 过程分析         2.3 代码实现         2.4 复杂度/稳定性分析 三. 希尔排序(缩小增量排序)         3.1 基本思想         3.2 过程分析          3.3 代码实现          3.4 复杂度/稳定性分析  

    2024年02月14日
    浏览(42)
  • 数据结构:手撕图解七大排序(含动图演示)

    插入排序分为直接插入排序和希尔排序,其中希尔排序是很值得学习的算法 希尔排序的基础是直接插入排序,先学习直接插入排序 直接插入排序类似于打扑克牌前的整牌的过程,假设我们现在有2 4 5 3四张牌,那么应该怎么整牌? 方法很简单,把3插到2和4中间,这样就完成了

    2024年02月15日
    浏览(51)
  • [数据结构 -- 手撕排序第二篇] 一篇带你详细了解希尔排序

    目录 1、常见排序算法 1.1 插入排序基本思想 2、希尔排序 2.1 希尔排序( 缩小增量排序 ) 2.1.1 预排序阶段 2.1.2 插入排序阶段 2.2 单趟希尔排序 2.2.1 思路分析 2.2.2 代码实现 3、希尔排序代码实现 4、希尔排序时间复杂度 5、希尔排序与插入排序效率对比 6、希尔排序特性总结 直接

    2024年02月13日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包