【数据结构】归并排序 —— 递归及非递归解决归并排序

归并排序

  • 一、归并排序
    • 1、归并排序的思想
    • 2、归并排序代码实现(递归)
      • <1> 归并排序的递归区间
      • <2> 归并排序的稳定性
      • <3> 拷贝
    • 3、归并排序代码实现(非递归)
      • <1> 循环区间溢出问题
  • 二、总结

一、归并排序

1、归并排序的思想

假设我们要排一个升序的数组
先看下面的动图
在这里插入图片描述

假设有两组升序数据
我们设置 ptr1 和 ptr2 来代表数组下标
将两数据进行比较,将小的填入一个新的数组
当两个数组中的数都进入新的数组
接下来我们只需要将该数组复制到原数组中
就完成了排序

如果我们要排一个升序数组
就要将它分成两个小的升序数组

若想得到两个小的升序数组
就要将每一个小的升序数组分成两个更小的升序数组

。。。。。。

————————————————————————————
于是我们想到了用递归的方式
一层层递归
直到只剩下一个不用数据,然后返回进行排序

2、归并排序代码实现(递归)

void _MergeSort(int* a, int* tmp, int left, int right)
{
	//返回停止条件
	if (left == right)
	{
		return;
	}

	//取中间值
	int mid = (left + right) / 2;
	
	//递归
	//[left, mid][mid + 1, right]
	//递归左边
	_MergeSort(a, tmp, left, mid);
	//递归右边
	_MergeSort(a, tmp, mid + 1, right);

	//进行单次归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;

	//记录起始位置
	int begin = left, end = right;

	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[begin++] = a[begin1++];
		}
		else 
		{
			tmp[begin++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[begin++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[begin++] = a[begin2++];
	}
	


//进行拷贝
    memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));

}

//归并排序
void MergeSort(int* a, int left, int right)
{
	//开辟一个复制数组
	int* tmp = (int*)malloc(sizeof(int) * (right - left + 1));
	if (tmp == NULL)
	{
		perror("malloc fail:");
		exit(1);
	}

	//进行归并排序
	_MergeSort(a, tmp, left, right);

	//数组销毁
	free(tmp);
	tmp = NULL;
}

<1> 归并排序的递归区间

递归区间1
[left, mid][mid + 1, right]
递归区间2
[left, mid - 1][mid, right]

看这两个区间有所不同
上面代码我采用的是区间1
而没有采用区间2
因为区间2会出现死循环,导致栈溢出
看下面的图:

区间1:
在这里插入图片描述
区间2:
在这里插入图片描述
同样的 mid 但是当递归至只剩下两个时,区间2就会陷入死循环

<2> 归并排序的稳定性

while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[begin++] = a[begin1++];
		}
		else 
		{
			tmp[begin++] = a[begin2++];
		}
	}

在比较 begin1 和 begin2 时我们用的是 " <= "
这样前面的数就会在前面
在这里插入图片描述
在图上我们发现我们在面对相同的数时,我们会先将 ptr1 中的数填入数组,那么相同数的相对位置没有发生改变,我们说归并排序是稳定的

<3> 拷贝

//进行拷贝
memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));

我们是边排序边拷贝的
如果等到最后拷贝会有出问题的风险
memcpy函数

3、归并排序代码实现(非递归)

如果是用非递归的方式
我们可以用循环
设置gap变量,gap 每次循环结束 gap *= 2
完成整个数组的归并
在这里插入图片描述

代码实现:

//归并排序(非递归)
void MergeSortNonR(int* a, int left, int right)
{
	//开辟复制数组
	int* tmp = (int*)malloc(sizeof(int) * (right - left + 1));
	if (tmp == NULL)
	{
		perror("malloc fail:");
		exit(1);
	}

	int gap = 1;
	//进行单次排序
	while (gap < right + 1)
	{
		for (int i = 0; i < right + 1; i += gap * 2)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + gap * 2 - 1;
			int begin = i;
			printf("[%d, %d][%d, %d]", begin1, end1, begin2, end2);
			
			//当begin2超过数组区间
			if (begin2 > right)
			{
				break;
			}
			//当只有end2超过区间
			if (end2 > right)
			{
				end2 = right;
			}

			//单次归并
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[begin++] = a[begin1++];
				}
				else
				{
					tmp[begin++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[begin++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[begin++] = a[begin2++];
			}
			//将数组进行复制
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		printf("\n\n");
		gap *= 2;
	}

	//销毁复制数组
	free(tmp);
	tmp = NULL;
}

<1> 循环区间溢出问题

当begin2超过数组区间
if (begin2 > right)
{
	break;
}
当只有end2超过区间
if (end2 > right)
{
	end2 = right;
}

在循环过程中,除了 begin1 不会超出数组区间
其他三个都有可能超出区间
我们将所有区间都打印出来
在这里插入图片描述
我们发现超出区间总共分为三种情况
在这里插入图片描述
当 begin2 和 end1 超出区间时:
说明后面的整个区间都超出了数组的范围
那就不用归并

当 end2 超出区间时:
说明后面的一部分数没有超出区间
可以将 end2 改为区间的最大值,继续进行归并

二、总结

归并排序的时间复杂度为 O(N * logN)
是比较快的排序
但是归并排序有它的缺陷
它的空间复杂度为 O(N)
排序需要开辟空间
当排序数量过大时有风险

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/919159.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单片机学习笔记 6. 数码管动态显示

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示 目录 0、实现的功能 1、Keil工程 1-1 数码管动态显示 1-2 数组的定义与引用…

go 学习网站,go例子 go demo go学习视频

1. 代码例子&#xff1a; Go by Example 2. b站 视频&#xff1a; 尚硅谷视频&#xff1a; 004_尚硅谷_程序的基本概念_哔哩哔哩_bilibili 3. go技术文档&#xff1a; fmt Go语言中文文档

MySQL(5)【数据类型 —— 字符串类型】

阅读导航 引言一、char&#x1f3af;基本语法&#x1f3af;使用示例 二、varchar&#x1f3af;基本语法&#x1f3af;使用示例 三、char 和 varchar 比较四、日期和时间类型1. 基本概念2. 使用示例 五、enum 和 set&#x1f3af;基本语法 引言 之前我们聊过MySQL中的数值类型&…

# 06_ Python基础到实战一飞冲天(二)-python基础(六)--变量的使用与变量类型

06_ Python基础到实战一飞冲天&#xff08;二&#xff09;-python基础&#xff08;六&#xff09;–变量的使用与变量类型 一、程序执行原理-06-明确程序的作用 1、程序的作用 程序就是 用来处理数据 的&#xff01; 2、各类软件的场景&#xff1a; 新闻软件 提供的 新闻内容…

人工智能之机器学习5-回归算法1【培训机构学习笔记】

培训内容&#xff1a; 模型评估 培训班上课的PPT里很多错误&#xff0c;即使讲了很多年也从没改正过来。 而且很多字母没有给出具体的解释&#xff0c;比如RSS和TSS&#xff0c;对初学者非常不友善。 个人学习&#xff1a; 分类和回归的区别 回归和分类是机器学习和统计学…

实验十三 生态安全评价

1 背景及目的 生态安全是生态系统完整性和健康性的整体反映&#xff0c;完整健康的生态系统具有调节气候净化污染、涵养水源、保持水土、防风固沙、减轻灾害、保护生物多样性等功能。维护生态安全对于人类生产、生活、健康及可持续发展至关重要。随着城市化进程的不断推进&…

nvm安装node遇到的若干问题(vscode找不到npm文件、环境变量配置混乱、npm安装包到D盘)

问题一&#xff1a;安装完nvm后需要做哪些环境变量的配置&#xff1f; 1.打开nvm文件夹下的setting文件&#xff0c;设置nvm路径和安装node路径&#xff0c;并添加镜像。 root: D:\software\nvm-node\nvm path: D:\software\nvm-node\nodejs node_mirror: https://npmmirror.c…

数据结构-树状数组专题(1)

一、前言 树状数组可以解决部分区间修改和区间查询的问题&#xff0c;相比于线段树&#xff0c;代码更加简单易懂 二、我的模板 搬运jiangly鸽鸽的模板&#xff0c;特别注意这个模板中所有涉及区间的都是左闭右开区间&#xff0c;且vector的有效下标都从0开始 template <…

Linux网络——套接字编程

1. 网络通信基本脉络 基本脉络图如上&#xff0c;其中数据在不同层的叫法不一样&#xff0c;比如在传输层时称为数据段&#xff0c;而在网络层时称为数据报。我们可以在 Linux 中使用 ifconfig 查看网络的配置&#xff0c;如图 其中&#xff0c;inet 表示的是 IPv4&#xff0c;…

‘视’不可挡:OAK相机助力无人机智控飞行!

南京邮电大学通达学院的刘同学用我们的oak-d-lite实现精确打击无人机的避障和目标识别定位功能&#xff0c;取得了比赛冠军。我们盼望着更多的朋友们能够加入到我们OAK的队伍中来&#xff0c;参与到各式各样的比赛中去。我们相信&#xff0c;有了我们相机的助力&#xff0c;大家…

复旦微电子FM33LC046U在keil工程中无法使用j-link下载问题解决

在Keil环境下使用JLINK工具下载程序&#xff0c;发现J-link V7.89a无法识别FM33LC046U&#xff0c;提示如下&#xff1a; 选择Cortex-M0 设置为SW模式&#xff0c;即可识别到芯片 经过如上步骤&#xff0c;就可以使用Jlink下载和仿真程序

java中设计模式的使用(持续更新中)

概述 设计模式的目的&#xff1a;编写软件过程中&#xff0c;程序员面临着来自耦合性&#xff0c;内聚性以及可维护性&#xff0c;可扩展性&#xff0c;重用性&#xff0c;灵活性等多方面的挑战&#xff0c;设计模式是为了让程序&#xff08;软件&#xff09;&#xff0c;具有…

【计算机网络实验】之静态路由配置

【计算机网络实验】之静态路由配置 实验题目实验目的实验任务实验设备实验环境实验步骤路由器配置设置静态路由测试路由器之间的连通性配置主机PC的IP测试 实验题目 静态路由协议的配置 实验目的 熟悉路由器工作原理和机制&#xff1b;巩固静态路由理论&#xff1b;设计简单…

【PS】矢量绘图技巧

1、先使用钢笔工具结合ctrl和alt建将苹果大致扣出来。 任意选择一个颜色进行填充 新建一个图层&#xff0c;使用渐变工具为图层添加渐变颜色 选择剪切蒙版&#xff0c;将图层颜色填入苹果&#xff0c;得最终结果。 内容二、麦当劳 与内容一类似的&#xff0c;使用钢笔工具将M形…

【HCIP]——OSPF综合实验

题目 实验需求 根据上图可得&#xff0c;实验需求为&#xff1a; 1.R5作为ISP&#xff1a;其上只能配置IP地址&#xff1b;R4作为企业边界路由器&#xff0c;出口公网地址需要通过PPP协议获取&#xff0c;并进行CHAP认证。&#xff08;PS&#xff1a;因PPP协议尚未学习&#…

django启动项目报错解决办法

在启动此项目报错&#xff1a; 类似于&#xff1a; django.core.exceptions.ImproperlyConfigured: Requested setting EMOJI_IMG_TAG, but settings are not c启动方式选择django方式启动&#xff0c;以普通python方式启动会报错 2. 这句话提供了对遇到的错误的一个重要线索…

【GeekBand】C++设计模式笔记12_Singleton_单件模式

1. “对象性能” 模式 面向对象很好地解决了 “抽象” 的问题&#xff0c; 但是必不可免地要付出一定的代价。对于通常情况来讲&#xff0c;面向对象的成本大都可以忽略不计。但是某些情况&#xff0c;面向对象所带来的成本必须谨慎处理。典型模式 SingletonFlyweight 2. Si…

计算机网络 (1)互联网的组成

一、互联网的边缘部分 互联网的边缘部分由所有连接在互联网上的主机组成&#xff0c;这些主机又称为端系统&#xff08;end system&#xff09;。端系统可以是各种类型的计算机设备&#xff0c;如个人电脑、智能手机、网络摄像头等&#xff0c;也可以是大型计算机或服务器。端系…

电商行业客户服务的智能化:构建高效客户服务知识库

在电商行业&#xff0c;客户服务是提升用户体验和品牌忠诚度的关键。随着数字化转型的深入&#xff0c;构建一个高效的客户服务知识库变得尤为重要。本文将探讨电商行业如何构建客户服务知识库&#xff0c;并分析其在提升服务质量中的作用。 客户服务知识库的重要性 客户服务…

CentOS 9 无法启动急救方法

方法一&#xff1a;通过单用户安全模式启动 开机按上下方向键&#xff0c;选择需要启动的内核&#xff0c;按e键进入配置模式 修改配置 ro 改 rw 删除 rhgb quiet 末尾增加 init/bin/bash 按 Ctrlx 启动单用户模式 如果想重新启动&#xff0c;重启电脑 执行 exec /sbin/in…