c语言-归并排序

news2025/1/16 17:45:17

       

目录

1、归并排序基本思想

2、归并排序的实现(递归法)

2.1 代码实现递归法归并排序 

3、归并排序的实现(非递归法)

3.1 修正边界问题

 3.2 代码实现非递归法归并排序 

结语: 


前言:

        归并排序是一种把数组排成有序数组的分治算法,其逻辑和归并操作是一样的,就是把两个子序列合并成一个子序列,只不过归并排序是把两个有序的子序列合并成一个有序的子序列,最后完成对数组的排序。

1、归并排序基本思想

        归并排序就是把一个数组看成是由两个子序列构成,这两个子序列再继续细分,是由四个子序列构成,四个子序列又分成由八个子序列构成...如此细分下去直到一个元素代表一个序列。这样就能够将两个子序列合并成一个序列,因为此时一个序列代表一个元素,可以直接进行元素的比较,从而构成一个新的含两个元素的有序序列。

2、归并排序的实现(递归法)

         首先把数组分成两个左右区间(既序列),然后这两个左右区间再进行分区间。

        总逻辑图:

        可以从上图中看到,对一个数组不断进行分解,直到分解至最后一个元素就进行归并,该结构类似二叉树的递归结构,从递归的思想出发,也就是当递归到最后一个元素的时候,则无需再递归,递归函数逐渐向上”回收“,并且完成排序。       


        但是如果在原数组内进行分解和归并等操作则过于复杂,因此动态开辟一个新的数组temp,让相邻元素组进行归并的时候都在数组temp上完成,最后再把新数组temp上的数据用memcpy拷贝到原数组上覆盖原先的元素的顺序,达到对原数组排序的效果。


        然后把数组temp中元素拷贝到原数组内,这样原数组的元素顺序就得到了改变。 


        当然以上只是一部分的操作,整体逻辑与上相同,并且重复以上操作。

        最后一步就是对1 5 6 10和2 3 4 9这两组进行归并,得到的序列就是最终的有序序列。


        不过在此要还要注意拷贝到temp数组时,要拷贝到对应的位置上。

2.1 代码实现递归法归并排序 

        代码如下:



#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void _MergeSort(int* a, int left, int right, int* temp)//归并排序
{
	if (left == right)//left==right说明递归至只有一个元素,开始向上“收回”
		return;
	int mid = (left + right) / 2;//按照mid让数组分成两个区间

	//递归函数
	_MergeSort(a, left, mid, temp);
	_MergeSort(a, mid + 1, right, temp);

	//begin1表示要进行归并的第一组元素的第一个元素,end1表示该组元素的最后一个元素
	//begin2表示要进行归并的第二组元素的第一个元素,end2表示该组元素的最后一个元素
	//begin1、end1和begin2、end2的作用就是让元素组两两进行归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;

	int i = left;//i的值temp数组的下标
	while (begin1 <= end1 && begin2 <= end2)//归并操作
	{
		if (a[begin1] > a[begin2])//小的元素先放到temp数组中
			temp[i++] = a[begin2++];
		else//其他情况则放在后面
			temp[i++] = a[begin1++];
	}

	//走到这里表示有一个元素组为空,则直接把另一个元素组移到temp数组中即可
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[i++] = a[begin2++];
	}
	//最后从temp数组中拷贝至原数组中要严格按照位置拷贝,left表示当前归并的位置
	memcpy(a + left, temp + left, sizeof(int) * (right - left + 1));
}
void MergeSort(int* a, int n)//归并排序
{
	int* temp = (int*)malloc(sizeof(int) * (n + 1));//开辟新数组
	if (temp == NULL)
	{
		perror("malloc");
		return;
	}
	_MergeSort(a, 0, n, temp);
	free(temp);
}

void PrintArr(int* a, int n)//打印数组
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void Test_MergeSort()//归并排序
{
	int arr[] = { 10,5,6,1,2,4,3,9 };
	int sz = sizeof(arr) / sizeof(int);
	PrintArr(arr, sz);
	MergeSort(arr, sz - 1);
	PrintArr(arr, sz);
}

int main()
{
	Test_MergeSort();
	return 0;
}

        运行结果:

        从结果看出递归法可以完成归并排序。 

3、归并排序的实现(非递归法)

        非递归方法思路图如下:

        从上图可以看出,非递归法与递归法的区别:只不过是把层层递归至只剩一个元素一组的思想换成了一开始就从一个元素一组往后不断增加每组的元素个数。其中,要进行归并的元素组1的第一个元素依然是begin1,最后一个元素是end1。元素组2的第一个元素依然是begin2,end2。

        但是非递归法再面对数组有九个元素的时候,就不能像以上一样每组元素完整的对比了。

3.1 修正边界问题

        不过非递归法再处理数组元素个数不为2的幂次方时,比如对要排序的数组的元素个数为9个时,会出现越界问题。

        这里begin2和end2的值明显是超出了数组的最后一个元素的下标值,因此如果访问begin2、end2会发生越界。所以这里采取让begin2的值大于end2的值,让他们成为一个不存在的区间,因此在代码中就不会对他们进行访问了(结合上述代码中归并操作while循环条件)。


        从上文的非递归思路图来看,当数组中有8个元素,则在gap=4的时候就完成了对数组的排序,也可以理解为当gap=8时结束排序操作。但是若数组中有9个元素,则gap=8时并不会结束排序操作,而是会再进行一轮排序,而该轮排序就是对第9个元素进行归并的关键一轮排序,所以在gap=8之前我们要让第9个元素和与他对比的元素组不参与进来,等到gap=8时,在进行对end2的修正从而间接的让第9个元素进行归并。

 3.2 代码实现非递归法归并排序 

        代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void _MergeSortNon(int* a, int n, int* temp)//归并排序(非递归法)
{
	int gap = 1;//从一个元素一个元素组开始
	while (gap < n)//gap>=n时说明排序完成
	{
		int j = 0;//temp数组下标
		for (int i = 0; i < n; i += 2 * gap)//元素组1和下一个元素组1直接的间隔是2*gap
		{
			//设置元素组1和元素组2的第一个元素和最后一个元素下标
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			//修正
			if (end1 >= n)//当end1超出范围则表示begin2和end2也超出范围
			{
				end1 = n - 1;//修正end1让其等于begin1,表示该元素在数组中没有变动

				//让元素组2的begin2>end2,目的不让其进入下面的while循环,也就不会发生越界了
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)//目的也是不然元素组2进入while循环
			{
				begin2 = n;
				end2 = n - 1;
			}
			else if (end2 >= n)//来到这局代码,说明begin2<n,这时修正end2让其等于begin2
			{
				end2 = n - 1;
			}

			//归并操作,和上文逻辑一样
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] > a[begin2])
					temp[j++] = a[begin2++];
				else
					temp[j++] = a[begin1++];
			}
			while (begin1 <= end1)
			{
				temp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[j++] = a[begin2++];
			}
		}
		memcpy(a, temp, sizeof(int) * n);//最后拷贝数组即可
		gap *= 2;
	}
}
void MergeSortNon(int* a, int n)//归并排序(非递归法)
{
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc");
		return;
	}
	_MergeSortNon(a, n, temp);
	free(temp);
}

void PrintArr(int* a, int n)//打印数组
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void Test_MergeSortNon()//归并排序(非递归法)
{
	int arr[] = { 10,5,6,1,2,4,3,9,0, };
	int sz = sizeof(arr) / sizeof(int);
	PrintArr(arr, sz);
	MergeSortNon(arr, sz);
	PrintArr(arr, sz);
}

int main()
{
	Test_MergeSortNon();
	return 0;
}

        运行结果:

        从结果中可以看出非递归法也能实现归并排序。

结语: 

         以上就是关于归并排序的介绍,归并排序的递归法和非递归法主要还是以归并操作的逻辑为主。只是对于归并排序的非递归方法而言,其逻辑确实有些复杂,但是只要理解了边界修正的问题还是能够很好的理解此方法的,最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充~!!谢谢大家!!(~ ̄▽ ̄)~

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

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

相关文章

go并发编程(中)

目录 一、并发安全性 1.1 变量并发安全性 1.2 容器并发安全性 二、多路复用 三、协程常见的面试题 3.1交替打印奇数偶数 一、并发安全性 1.1 变量并发安全性 这个和C中并发安全是一样的&#xff0c;主要是多个线程对临界资源的同时访问&#xff0c;最经典的就是 n操作…

MQ - 消息系统

消息系统 1、消息系统的演变 在大型系统中&#xff0c;会需要和很多子系统做交互&#xff0c;也需要消息传递&#xff0c;在诸如此类系统中&#xff0c;你会找到源系统&#xff08;消息发送方&#xff09;和 目的系统&#xff08;消息接收方&#xff09;。为了在这样的消息系…

Java 连接数据库

数据库&#xff1a;存储数据&#xff08;集中管理&#xff09; 目的&#xff1a; 文件中的数据能够放在数据库中集中管理 管理方法&#xff1a;一个项目一个库&#xff0c;每个库中包含最小化数据的表 开发&#xff1a; 节省存储空间&#xff0c;节省运行空间&#xff0c;采…

【富文本编辑器】原生JS使用WangEditor和vue上传图片前后端demo

【富文本编辑器】原生JS使用WangEditor上传图片前后端demo 第一步 HTML 第二步 初始化WangEditor与图片上传回调函数 第三步 后端返回数据体封装 第四步 后端接口上传图片&#xff0c;并返回图片地址 最近&#xff0c;我遇到了这样一个问题&#xff1a;因为我们的项目是基于…

SDGAN:一种用于低剂量CT图像重建的新型空间可变形生成对抗性网络

SDGAN: A novel spatial deformable generative adversarial network for low-dose CT image reconstruction SDGAN&#xff1a;一种用于低剂量CT图像重建的新型空间可变形生成对抗性网络背景贡献实验方法The conformer-based generatorThe dual-scale discriminatorThe spatia…

C/S与B/S的区别

B/S与C/S理解 C/S结构B/S结构问题数据放在服务器端与客户端的利弊 C/S结构 客户端&#xff1a;用户安装的软件 服务端&#xff1a;统一管理数据库的主机中的软件 叫做服务端。 B/S结构 用户通过浏览器实现&#xff08;往往表示业务逻辑在前端进行实现&#xff0c;主要业务逻…

栈和队列的OJ题——14.用栈实现队列

14.用栈实现队列 232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 此题可以用两个栈实现&#xff0c;一个栈进行入队操作&#xff0c;另一个栈进行出队操作 出队操作&#xff1a; 当出队的栈不为空是&#xff0c;直接进行出栈操作&#xff…

基于SSM的网上手机销售系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

OpenSSH(CVE-2023-38408)OpenSsh9.5一键升级修复

yum install -y git cd /root git clone https://gitee.com/qqmiller/openssh-9.5p1-.x86_64.git cd openssh-9.5p1-.x86_64/ bash openssh_update.sh重启sshd&#xff1a; systemctl restart sshd 查看sshd状态&#xff1a; systemctl status sshd 重要的是按此操作升级完成…

一文彻底弄懂动态规划【DP】

动态规划是一种重要的算法&#xff0c;它能解决很多看似复杂的问题&#xff0c;关键在于找到问题的子问题结构&#xff0c;并根据子问题的解决方式来解决原问题。首先要了解的是动态规划的基本思想&#xff1a; 动态规划的基本思想是&#xff1a;将一个复杂的问题分解为一系列…

SpringBootAdmin

SpringBootAdmin 文章目录 SpringBootAdmin创建SpringBootAdmin服务端创建SpringBootAdmin客户端启动应用 总结 github地址 https://github.com/codecentric/spring-boot-admin 可以查到所有的版本号 创建SpringBootAdmin服务端 创建springBoot项目的时候&#xff0c;在ops选项…

图文并茂教你模拟302接口,实现js中axios,fetch遇到302状态码后跳转的多种方案axios,fetch成功响应拦截302

前情提要 日常工作中&#xff0c;我们会使用fetch,或者axios发起请求来获取数据&#xff0c;但是当我们遇到一些特殊需求的时候&#xff0c;使用不同库之后&#xff0c;会得到不同的结果&#xff0c;例如302,308的状态码&#xff0c;那么我们应该怎么处理这两种情况呢&#xf…

【Linux】第二十六站:软硬链接

文章目录 一、软链接二、硬链接三、ln命令四、该如何理解硬链接&#xff1f;五、如何理解软链接六、为什么要用软硬链接1.软链接的应用场景2.硬链接的应用场景 一、软链接 如下所示&#xff0c;我们创建一个文件以后&#xff0c;然后执行下面的指令 ln -s file.txt soft-link…

Prime 2.0

信息收集 # Nmap 7.94 scan initiated Thu Nov 23 20:09:06 2023 as: nmap -sn -oN live.nmap 192.168.182.0/24 Nmap scan report for 192.168.182.1 Host is up (0.00018s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report for 192.168.182.2 Host is u…

P-Tuning v2论文概述

P-Tuning v2论文概述 P-Tuning v2论文概述前言微调的限制性P-Tuning的缺陷P-Tuning v2 摘要论文十问NLU任务优化点实验数据集预训练模型实验结果消融实验 结论 P-Tuning v2论文概述 前言 微调的限制性 微调&#xff08;fine-tuning&#xff09;是一种在预训练模型基础上进行目…

使用 Kettle 完成数据 ETL

文章目录 使用 Kettle 完成数据 ETL数据清洗数据处理 使用 Kettle 完成数据 ETL 现在我们有一份网站的日志数据集&#xff0c;准备使用Kettle进行数据ETL。先将数据集加载到Hadoop集群中&#xff0c;然后对数据进行清洗&#xff0c;最后加载到Hive中。 在本地新建一个数据集文…

Active Stereo Without Pattern Projector论文精读

1.背景补充 主动立体相机和被动立体相机的主要区别在于它们获取立体视觉信息的方式 主动立体相机12&#xff1a; 主动立体视觉是指寻找最佳的视角去重建目标或者场景1。主动视觉的实现方式通常有&#xff1a;改变环境中的光照条件、改变相机的视角、移动相机自身位置等&…

大模型中幂律缩放法则和涌现能力

幂律缩放法则是一种用于描述两个变量之间关系的数学模型。 根据幂律缩放法则&#xff0c;当一个变量的值变化时&#xff0c;另一个变量的值以指数方式变化。具体而言&#xff0c;幂律缩放法则可以表示为Y a * X^b&#xff0c;其中Y表示一个变量的值&#xff0c;X表示另一个变…

国民技术N32_MCU ADC如何获取高精度采样数据

前言 国民技术微控制器内置最多四个高级 12 位 ADC (取决于产品系列) &#xff0c;具有校准功能&#xff0c;用于提高环境条件 变化时的 ADC 精度。 在涉及模数转换的应用中&#xff0c; ADC 精度会影响整体的系统质量和效率。为了提高此精度&#xff0c;必须了解与 ADC 相关…

鸿蒙开发学习笔记

快速入门 配置网络权限 1.打开项目的 module.json5 文件 2.在module 里面写下面代码 3.这样就可以使用网络图片了 4.模拟器上就可以正常显示网络图片了 5.官方文档有相关说明 6. 华为官方编辑工具使用技巧&#xff08;内置文档&#xff09;&#xff0c;鼠标移动到标签上&…