深度理解排序算法——归并排序

这篇具有很好参考价值的文章主要介绍了深度理解排序算法——归并排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

…………………………………………………………………………………文章来源地址https://www.toymoban.com/news/detail-779825.html

归并排序的概念:

给定一段无序数组,将数组拆分成两段,使得左右两段得数组均呈现有序状态,再借助临时数组将两段数组归并至一块呈现有序,最后拷贝回原数组即得到有序数组。
从逻辑上理解,就是一颗二叉树,将初始数组拆分成两段,再将两段拆分成四段,依次下去直至到子树节点数为1为止停止,再通过后序遍历依依对小区间不断归并。

递归方式实现:

图解:
深度理解排序算法——归并排序,排序算法,算法,数据结构,经验分享,c语言
注:
声明指针left、right、mid。 left指向数组最左端元素,right指向数组最右端元素,mid=(left+right)/2 。
显然可以看出**[left,mid]为左区域**,[mid+1,right]为右区域
递归的结束条件是left=right返回值为true

这里是不会出现left>right的情况的,具体证明请读者自证,当然如果你把结束条件写成left>=right也是没有问题的。

代码演示:

void _MergeSort(int* a,int* tmp,int begin,int end){
	if(begin==end)
		return ;
	int left=begin,right=end;
	int mid=(left+right)/2;
	_MergeSort(a,tmp,left,mid); 
	_MergeSort(a,tmp,mid+1,right);
								//创建begin和end增加代码可读性
	int begin1=left,end1=mid;
	int begin2=mid+1,end2=right;
	int j=begin;
	while(begin1<=end1 && begin2<=end2){
		if(a[begin1]<a[begin2])
			tmp[j++]=a[begin1++];
		else
			tmp[j++]=a[begin2++];}
	while(begin1<=end1)
		tmp[j++]=a[begin1++];
	while(begin2<=end2)
		tmp[j++]=a[begin2++];
	memcpy(a+begin,tmp+begin,sizeof(int)*(end-begin+1));}
	

void MergeSort(int* a,int n){       //n为数组大小
	int* tmp=(int*)malloc(sizeof(int)*n);
	//建立临时数组,后续拷贝要用到 (中转站)
	_MergeSort(a,tmp,0,n-1);
	free(tmp);tmp=NULL;}

补充:
归并过程图解:
深度理解排序算法——归并排序,排序算法,算法,数据结构,经验分享,c语言

优化算法:

递归的方式最大的缺点就是建立函数栈帧非常多,存在栈溢出的风险(当然只要代码逻辑正确这种情况是较少出现的),但是建立过多的栈帧可能会导致时间代价增加
因此我们采取一种小区间优化的方式来减少大量的栈帧
只需要增加一个递归结束条件即可
对于数组大小小于10的区间采取插入排序的方式处理

if(end-begin+1<10)
	{InsertSort(a,end-begin+1);//插入排序
	return;}

虽然只是3行的代码,但却能够减少%80多的栈帧
证明:
深度理解排序算法——归并排序,排序算法,算法,数据结构,经验分享,c语言

当然这只能增加一点点的速度,并不会带来质变性提升,但对空间来说大大减少了负担

迭代方式实现:

迭代方式要比递归方式复杂多得多,原因是因为我们需要考虑很多越界情况并作出调整
我们由易入难,先举数组大小为8的特例引出雏形
图解:
深度理解排序算法——归并排序,排序算法,算法,数据结构,经验分享,c语言
代码演示:

void MergeSortNonR(int* a,int n){
	int* tmp =(int*)malloc(sizeof(int)*n);
	int gap=1;//每个区域所含元素个数
	while(gap<n){
		int j=0;//管理tmp
		for(int i=0;i<n,i+=2*gap){
			int begin1=i,end1=i+gap-1;//左区域
			int begin2=i+gap,end2=i+2*gap-1;//右区域
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
					tmp[j++] = a[begin1++];
				else
					tmp[j++] = a[begin2++];
			}
			while (begin1 <= end1)
				tmp[j++] = a[begin1++];
			while (begin2 <= end2)
				tmp[j++] = a[begin2++];
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
			//归并一段拷贝一段
		}
		gap *= 2;
	}
	free(tmp); tmp = NULL;}

显然,上述代码块,存在非常大的局限性,它只能作用于数组大小是2^x个的数组,倘若是别的大小,那么它一定会出现指针越界的问题

下面我们来具体分析一下各种越界情况:

首先begin1是绝对不会出现越界的,因为每次的begin1初始值是i,而i必须小于n才能够进入循环体

深度理解排序算法——归并排序,排序算法,算法,数据结构,经验分享,c语言
因此我们必须对各种越界情况作出相应调整,

法一:

针对end1、begin2、end2或begin2、end2越界的情况,我们直接break,不让程序进入 while (begin1 <= end1 && begin2 <= end2) 的循环体(不对其进行归并),而针对end2越界的情况,我们只需要把end2调整为n-1即可进行归并

	if(end1>=n || begin2>=n)
		break;
	if(end2>=n)
		end2=n-1;

法二:

不通过break的方式,直接改变end1,begin2,end2的值使其不进入 while (begin1 <= end1 && begin2 <= end2)
针对第一种情况:
end1=n-1,begin2=n,end2=n-1;
针对第二种情况:
begin2=n,end2=n-1;
针对第三章情况:
end2=n-1;

**补充:对于第一第二种情况,其实只要把begin2调整至大于end2即可(begin2=1,end2=0也对)

在这种方式下,可以除了采取归并一段拷贝一段的方式,还可以采取一次gap循环结束后整体拷贝(法一会出现数据丢失,请读者自行证明)
即memcpy(a,tmp,sizeof(int)*n);

时间复杂度、空间复杂度:

平均向下建立logN个栈帧,开辟了大小为N的额外空间,故空间复杂度为O(N)
每一次归并的时间复杂度为O(N)
故总时间复杂度为O(NlogN)
可以看出归并排序是一种效率非常不错的排序算法。

文中若有错误欢迎读者指出,下期将给大家带来计数排序
觉得可以的话来个三连吧,蟹蟹!
Over!

…………………………………………………………………………………

到了这里,关于深度理解排序算法——归并排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 数据结构算法--5 归并排序

    我们先看一下归并排序是怎么归并的  两个有序列表,有low指针指向2,high指针指向6,mid指针指向9 再建一个新列表,12,所以1放到列表,右指针右移一位,再比较2和3,2放入列表,左指针右移一位,以此类推,肯定有一部分列表率先没有数,这时将另一列表直接append进入新

    2024年02月11日
    浏览(48)
  • 数据结构与算法:归并排序

    在讲解归并排序前,我们先看到一个问题: 对于这样两个有序的数组,如何将它们合并为一个有序的数组? 在此我们处理这个问题的思路就是:开辟一个新的数组,然后分别安置一个指针在左右数组,利用指针遍历数组,每次对比将比较小的那个元素插入到数组的尾部。 像

    2024年01月21日
    浏览(46)
  • python算法与数据结构---排序和归并排序

    掌握归并排序的基本原理 使用python语言解答归并排序题目 原理及过程 将两个有序的数组合并成一个有序数组称为 从上往下分解:把当前区间一分为二,直至分解为若干个长度为1的子数组 从上往下的合并:两个有序的子区域两两向上合并; 体现了分治思想,稳定排序 复杂

    2024年01月21日
    浏览(72)
  • 【数据结构】排序算法(二)—>冒泡排序、快速排序、归并排序、计数排序

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.冒泡排序 2.快速排序 2.1Hoare版 2.2占坑版 2.3前后指针版 2.4三数取中对快速排序的优化 2.5非递归版 3.归

    2024年02月08日
    浏览(54)
  • 【数据结构与算法】:非递归实现快速排序、归并排序

    🔥 个人主页 : Quitecoder 🔥 专栏 :数据结构与算法 上篇文章我们详细讲解了递归版本的快速排序,本篇我们来探究非递归实现快速排序和归并排序 快速排序的非递归实现主要依赖于栈(stack)来模拟递归过程中的函数调用栈。递归版本的快速排序通过递归调用自身来处理子

    2024年03月24日
    浏览(55)
  • [数据结构 -- 手撕排序算法第七篇] 递归实现归并排序

    目录 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 空间复杂

    2024年02月16日
    浏览(50)
  • 【一起学数据结构与算法】几种常见的排序(插入排序、选择排序、交换排序、归并排序)

    排序是计算机内经常进行的一种操作,其目的是将一组 “无序” 的记录序列调整为 “有序” 的记录序列。分 内部排序 和 外部排序 ,若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能

    2023年04月09日
    浏览(46)
  • 深度理解排序算法——归并排序

    ………………………………………………………………………………… 给定一段无序数组,将数组拆分成 两段 ,使得 左右两段 得数组均呈现有序状态,再借助 临时数组 将两段数组归并至一块呈现有序,最后 拷贝 回原数组即得到有序数组。 从逻辑上理解,就是一颗 二叉

    2024年02月03日
    浏览(36)
  • 【Java数据结构与算法】Day2-高级排序(希尔、归并、快速、计数)

    ✅作者简介:热爱Java后端开发的一名学习者,大家可以跟我一起讨论各种问题喔。 🍎个人主页:Hhzzy99 🍊个人信条:坚持就是胜利! 💞当前专栏:【Java数据结构与算法】 🥭本文内容:Java数据结构与算法中的比较高级的排序,希尔排序、归并排序、快速排序、计数排序

    2024年02月02日
    浏览(64)
  • 【数据结构】- 排序(详细介绍几种排序算法!!!*直接插入排序,*希尔排序,*选择排序,*堆排序,*冒泡排序,*快速排序,*归并排序)

    排序无处不在,所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 内部排序 :数据元素全部放在内存中的排序。 外部排序 :数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。 今天

    2024年01月20日
    浏览(62)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包