数据结构:归并排序

news2024/11/26 8:31:30

归并排序

时间复杂度O(N*logN)

如果两个序列有序,通过归并,可以让两个序列合并后也有序,变成一个有序的新数组

对于一个数组,如果他的左右区间都有序,就可以进行归并了

归并的方法

将数组的左右两个有序区间比较,每次都取出一个最小的,然后放入临时数组(不能在原数组上修改,因为修改原数组需要挪动数据,时间复杂度高)

但是对于任意一个数组而言,他的左右区间并不有序,如果想要使用归并排序,就要想办法取出数组的有序区间

这里可以使用递归拆分数组,当数组足够小(每一个区间都只有一个数据时),该区间就一定有序,就可以使用归并排序了

可以设置一个temp数组,用来存储分解的区间并进行排序,归并排序完成后再将temp拷贝回原数组的对应区间,直到递归完全结束(原数组有序)

代码

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;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	//判断哪个区间全部放入tmp,再将另一个区间全部放入tmp
	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);

	//释放tmp
	free(tmp);
	tmp = NULL;
}

归并排序的非递归方法

归并排序需要将递归改为循环

不方便使用栈来实现,因为不同于快速排序类似前序遍历,归并排序的递归类似于二叉树的后序遍历

问题核心

归并排序需要两个区间进行归并,

而如果使用栈,则当取到需要归并的两个区间的第二个

第一个区间就已经被pop出栈了,无法知道哪个区间需要和当前区间归并

解决方法

可以将数组看作一个一个数(一个数就是一个区间),然后每两个就进行归并

将归并后的两个数再看作一个区间,再与其他区间进行归并

即直接从叶子节点向前走(回溯)

可以设置gap = 2^n来归并每组数据

设置每个区间的首尾为b n(begin n),e n(end n)

eg.

由图可知,e1 = b1 + gap - 1,b2 = b1 + gap

后续同理

基础代码框架(存在问题)

根据当前的思路,可以写出大概的代码

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

	int gap = 1;
	while (gap < n)//一直归并到原数组大小
	{
		for (int j = 0; j < n; j += 2 * gap)//保证每一个区间都被取到
		{
			int begin1 = j, end1 = j + gap - 1;
			int begin2 = j + gap, end2 = begin2 + gap - 1;
			int i = j;

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[i++] = a[begin1++];
				}
				else
				{
					tmp[i++] = a[begin2++];
				}
			}
			//判断哪个区间全部放入tmp,再将另一个区间全部放入tmp
			while (begin1 <= end1)
			{
				tmp[i++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[i++] = a[begin2++];
			}

			memcpy(a + j, tmp + j, sizeof(int) * 2 * gap);
		}
		gap *= 2;
	}
	free(tmp);
	tmp == NULL;
}

代码问题(数组越界)

当前代码可能存在数组的越界访问

理论上来讲,除了begin1 = j < n外,其他的begin2, end1, end2都可能越界

因此需要分开处理这些问题

1.end1越界

end1越界(end1后面就没有数据了)说明当前没有第二组数据,而第一组只有一部分,且有序

因此当前这一组就不需要再进行归并了,直接让这一组准备下一层归并

2.begin2越界

begin2越界也不用归并,原因与end1越界相同

3.end2越界

end2越界需要归并,因为在begin2与end2中还存在部分有序数据,因此需要归并组成新区间

但是想要归并就需要修正end2,保证end2不再越界

4.修改后的代码

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

	int gap = 1;
	while (gap < n)//一直归并到原数组大小
	{
		for (int j = 0; j < n; j += 2 * gap)//保证每一个区间都被取到
		{
			int begin1 = j, end1 = j + gap - 1;
			int begin2 = j + gap, end2 = begin2 + gap - 1;
			int i = j;

			if (end1 >= n || begin2 >= n)//end1和begin2越界,跳出当前循环,不归并
			{
				break;
			}

			if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[i++] = a[begin1++];
				}
				else
				{
					tmp[i++] = a[begin2++];
				}
			}
			//判断哪个区间全部放入tmp,再将另一个区间全部放入tmp
			while (begin1 <= end1)
			{
				tmp[i++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[i++] = a[begin2++];
			}

			memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));//使用修正后的区间长度
		}
		gap *= 2;
	}
	free(tmp);
	tmp == NULL;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于8086温度采集系统仿真设计

**单片机设计介绍&#xff0c;基于8086温度采集系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于8086的温度采集系统仿真设计是一个综合性的项目&#xff0c;它结合了微处理器技术、传感器应用以及仿真技术&#…

紫光展锐P7885核心板详细参数介绍_5G安卓智能模块开发方案

紫光展锐P7885核心板采用了先进的6nm EUV制程工艺&#xff0c;集成了高性能的应用处理器和金融级安全解决方案&#xff0c;为用户带来了全新的性能体验。 P7885核心板搭载了先进的6nm制程工艺SoC P7885&#xff0c;其中包含四核A76和四核A55&#xff0c;主频可达2.7Ghz&#xf…

【一步一步学】RouterOS Mesh介绍,小白也能懂。

理论介绍 RouterOS Mesh是一种网络拓扑结构&#xff0c;它利用无线技术将多个路由器连接在一起&#xff0c;形成一个覆盖范围更广、信号更稳定的网络。这种结构可以帮助用户实现更好的网络覆盖和负载均衡&#xff0c;提高整体网络性能。 RouterOS Mesh的原理是通过建立多个路由…

JavaEE 初阶篇-深入了解单例模式(经典单例模式:饿汉模式、懒汉模式)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 单例模式的概述 2.0 单例模式 - 饿汉式单例 2.1 关于饿汉式单例的线程安全问题 3.0 单例模式 - 懒汉式单例 3.1 关于懒汉式单例的线程安全问题 3.1.1 加锁 synchr…

Java | Leetcode Java题解之第4题寻找两个正序数组的中位数

题目&#xff1a; 题解&#xff1a; class Solution {public double findMedianSortedArrays(int[] A, int[] B) {int m A.length;int n B.length;if (m > n) { return findMedianSortedArrays(B,A); // 保证 m < n}int iMin 0, iMax m;while (iMin < iMax) {int…

枚举---算法

1、定义 枚举算法&#xff1a;也称之为穷举算法&#xff0c;这种算法就是在解决问题的时候去使用所有的方式去解决这个问题&#xff0c;会通过推理去考虑事件发生的每一种可能&#xff0c;最后推导出结果。优点&#xff1a;简单粗暴&#xff0c;它暴力的枚举所有可能&#xff…

每天五分钟计算机视觉:使用神经网络完成人脸的特征点检测

本文重点 我们上一节课程中学习了如何利用神经网络对图片中的对象进行定位,也就是通过输出四个参数值bx、by、bℎ和bw给出图片中对象的边界框。 本节课程我们学习特征点的检测,神经网络可以通过输出图片中对象的特征点的(x,y)坐标来实现对目标特征的识别,我们看几个例子。…

低噪声、轨至轨运算放大器芯片—— D721、D722、D724,适合用于音频领域

应用领域 D721、D722、D724是我们推荐的三款低噪声、轨至轨运算放大器芯片&#xff0c;其中D721为单运放&#xff0c;D722为双运放&#xff0c;D724为四运放。适合用于音频领域、传感器等的信号放大处理&#xff0c;比如K歌宝、音响、测距、滤波器、AD转换器前级信号处理等等。…

通过开发板来学习ROS2 21讲(基础环境配置)

通过开发板来学习ROS2 21讲&#xff08;基础环境配置&#xff09; 简介 ROS2 21讲是古月居倾力打造的ROS2 的入门学习视频&#xff0c;相信有很多小伙伴也是通过ROS2 21讲入门的ROS2。在学习过程中&#xff0c;大家有可能是使用虚拟机/PC机来运行ROS2上的案例&#xff0c;但是…

在SpringCloud2023中使用openfeign进行远程调用

你好&#xff0c;这里是codetrend专栏“SpringCloud2023实战”。 前言 feign之前是Netflix的一个子项目&#xff0c;由于停止了维护&#xff0c;spring继续维护了一个openfeign作为替代。Spring Cloud OpenFeign 具有以下优点&#xff1a; 简化微服务之间的调用&#xff0c;…

是否应该升级到ChatGPT 4.0?深度对比ChatGPT 3.5与4.0的差异

如果只是想简单地体验AI的魅力&#xff0c;感受大模型的独特之处&#xff0c;或是玩一玩文字游戏&#xff0c;那么升级至ChatGPT 4.0可能并非必需。然而&#xff0c;若你期望将AI作为提升工作学习效率的得力助手&#xff0c;那么我强烈建议你升级到ChatGPT 4.0。 如果你不知道…

【linux】lsof命令使用

1. 功能 lsof list open files, 列出被进程所使用的文件名称。 2. 基础语法 3. 参数含义 参数含义-a过滤出多个选项要同时满足的文件-U仅列出UNIX-like系统的socket文件类型。-u指定用户&#xff0c;比如-u atiaisi&#xff0c;会把用户atiaisi相关的进程使用的文件列出来。…

辽宁梵宁教育:设计领域的靠谱正规线上教育机构典范

辽宁梵宁教育&#xff0c;作为一家专注于学习设计的线上教育机构&#xff0c;近年来在业界崭露头角&#xff0c;赢得了广大学习者的认可和好评。接下来&#xff0c;本文将从多个维度详细阐述梵宁教育为何是一家靠谱且正规的线上教育机构。 梵宁教育在师资力量上表现出色。其拥有…

如何一键展示全平台信息?Python手把手教你搭建自己的自媒体展示平台

前言 灵感源于之前写过的Github中Readme.md中可以插入自己的js图片和动态api解析模块&#xff0c;在展示方面十分的美观&#xff1a; 这方面原理可以简化为&#xff0c;在Markdown中&#xff0c;你可以使用HTML标签来添加图像&#xff0c;就像这样&#xff1a; <tr><…

YOLOv9改进策略 :IoU优化| Inner-IoU基于辅助边框的IoU损失,高效结合新型边界框相似度度量(MPDIoU)| 二次创新

💡💡💡本文独家改进:Inner-IoU引入尺度因子 ratio 控制辅助边框的尺度大小用于计算损失,新型边界框相似度度量(MPDIoU)MPDIoU损失进行有效结合 💡💡💡适用场景:小目标数据集,进一步提升检测精度,强烈推荐 《YOLOv9魔术师专栏》将从以下各个方向进行创新: …

使用Flutter混淆技术保护应用隐私与数据安全

在移动应用开发中&#xff0c;保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具&#xff0c;帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆&#xff0c;并提供了相关的操作步骤和注意事项。 &#x1f4dd; 摘要 本…

酷得智能 提供多类型IC采购服务及全国性的方案开发定制

东莞市酷得智能科技有限公司成立于广东省东莞市松山湖高新产业园区&#xff0c;我们专注于电子类方案开发设计&#xff0c;提供多类型的IC采购服务。 方案定制服务包括以下几个方面&#xff1a; 功能定制&#xff1a;根据客户需求&#xff0c;我们可以为其定制各种有趣、富有挑…

存内计算是否可以应用于边缘计算

本篇文章聚焦存内计算应用&#xff0c;我们将从云边端计算各有优势出发&#xff0c;围绕边缘计算场景已有落地、赋能边缘计算存&#xff0c;算大有可为三个方面展开介绍&#xff0c;并围绕存算与边缘计算的结合应用展开构想与展望。 一.云边端计算 各有优势 云边端&#xff0…

游戏引擎架构01__引擎架构图

根据游戏引擎架构预设的引擎架构来构建运行时引擎架构 ​

【THM】SQL Injection(SQL注入)-初级渗透测试

简介 SQL(结构化查询语言)注入,通常称为 SQLi,是对 Web 应用程序数据库服务器的攻击,导致执行恶意查询。当 Web 应用程序使用未经正确验证的用户输入与数据库进行通信时,攻击者有可能窃取、删除或更改私人数据和客户数据,并攻击 Web 应用程序身份验证方法以获取私有数据…