当前位置: 首页 > news >正文

【数据结构】手撕归并排序(含非递归)

目录

一,归并排序(递归)

1,基本思想

 2,思路实现

二,归并排序(非递归)

1,思路实现

2,归并排序的特性总结:


一,归并排序(递归)

1,基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用;

已有序的子序列合并,得到完全有序的序列;

先使每个子序列有序,再使子序列段间有序,将两个有序表合并成一个有序表,称为二路归并;

归并排序核心步骤:

 2,思路实现

这个归并排序乍一看像一颗二叉树,事实也是如此,如上图所示我们需要不断的拆分直至拆成一个元素此时就是有序的然后再合并合并的时候不要选择原地合并(原地合并时间复杂度很高)需要开辟与数组同等大小的空间用来存放数据

主函数整体框架:

//归并排序
void MergerSort(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//开辟同等大小数组
	int* tmp = (int*)malloc((end - begin + 1)*sizeof(int));
	//归并
	Merger(arr, tmp, begin, end);
	free(tmp);
	tmp = NULL;
}

然后我们就要开始实现 Merger 函数,是数据归并了;

把数组拆分成一个数据后开始合并,刚开始一 一合并,然后二 二合并,然后四 四合并,直至全数组合并完;

//归并
void Merger(int* arr, int* tmp,int begin,int end)
{
	int mid = (begin + end) / 2;
	if (begin == end)
	{
		return;
	}
	//排序【begin,mid】& 【mid+1,end】
	Merger(arr, tmp, begin,mid);
	Merger(arr, tmp, mid+1, end);

	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = 0;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] <= arr[begin2])
		{
			tmp[i++] = arr[begin1++];
		}
		else
		{
			tmp[i++] = arr[begin2++];
		}
	}
	while(begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	//进行拷贝
	memcpy(arr + begin, tmp, (end - begin+1)*sizeof(int));
}

然后我们运行测试一下:

可以看到是有序的,选择排序就 OK 了;

其实跟二叉树的前序遍历有异曲同工之处,前后知识都是连贯起来的;

二,归并排序(非递归)

1,思路实现

现在我们来拿捏一下非递归版的归并排序,其实也还是换汤不换药;

其实新思路是这个图的下半部分,我们先让数据一 一合并,然后再二 二合并,然后再四 四合并程倍数增长有人问如果越界了怎么办?没关系我们后面会做越界处理的

直接上代码!

//归并排序(非递归)
void MergerSortNon(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//开辟同等大小数组
	int* tmp = (int*)malloc((end - begin + 1) * sizeof(int));
	int gap = 1;
	int j = 0;
	while (gap < end)
	{
		for (j = 0; j < end; j += 2 * gap)
		{
			int begin1 = j, end1 = begin1+gap-1;
			int begin2 =end1+1, end2 = begin2+gap-1;
			int i = 0;
			//处理边界问题
			if (end1 >= end)
			{
				break;
			}
			if (end2 >end)
			{
				end2 = end;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] <= arr[begin2])
				{
					tmp[i++] = arr[begin1++];
				}
				else
				{
					tmp[i++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[i++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[i++] = arr[begin2++];
			}
			//进行拷贝
			memcpy(arr + j, tmp, (end2 - j+ 1) * sizeof(int));
		}
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

我们来运行测试一下:

可以看到是有序的,选择排序就 OK 了;

2,归并排序的特性总结:

1, 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题

2, 时间复杂度:O(N*logN)

3, 空间复杂度:O(N)

4, 稳定性:稳定

第四阶段就到这里了,带大家继续吃肉!

后面博主会陆续更新;

如有不足之处欢迎来补充交流!

完结。。

相关文章:

  • Kafka:安装与简单使用
  • Java中的TCP通信(网络编程 二)
  • 使用华为eNSP组网试验⑷-OSPF多区域组网
  • 完美清晰,炫酷畅享——Perfectly Clear Video为你带来卓越的AI视频增强体验
  • Python之字符串格式化
  • 【Django 笔记】第一个demo
  • Linux-正则三剑客
  • 怎么将Linux上的文件上传到github上
  • 总结二:linux面经
  • PyTorch入门之【AlexNet】
  • [React源码解析] React的设计理念和源码架构 (一)
  • JMeter性能测试
  • [架构之路-228]:目标系统 - 纵向分层 - 计算机硬件与体系结构 - 硬盘存储结构原理:如何表征0和1,即如何存储0和1,如何读数据,如何写数据(修改数据)
  • 【17】c++设计模式——>原型模式
  • Raid10--Raid01介绍
  • 华为云云耀云服务器L实例评测|基于canal缓存自动更新流程 SpringBoot项目应用案例和源码
  • 关于Go语言的底层,Slice,map
  • 博弈论——伯特兰德寡头模型(Bertrand Model)
  • 利用fitnesse实现api接口自动化测试
  • Waves 14混音特效插件合集mac/win
  • 佩斯科夫:若普京认为必要,将公布土耳其谈判俄方代表人选
  • 外交部亚洲司司长刘劲松会见印度驻华大使罗国栋
  • 中国创面修复学科发起者之一陆树良教授病逝,享年64岁
  • 在对国宝的探索中,让美育浸润小学校园与家庭
  • 权益类基金发行回暖,这些老将挂帅新基,谁值得买?
  • 法治日报:炮制师生日常剧本,校园怎么成了短视频流量秀场?