c语言-归并排序

       

目录

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;
}

        运行结果:

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

结语: 

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

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

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

相关文章

万界星空科技灯具行业MES介绍

中国是LED照明产品最大的生产制造国&#xff0c;如今&#xff0c;我国初步形成了包括LED外延片的生产、LED芯片的制备、LED芯片的封装以及LED产品应用在内的较为完超为产业链&#xff0c;随着LED照明市场渗诱率的快速警升&#xff0c;LED下游应用市场将会越来越广阔。这也将推动…

硬件基础:二极管

基本定义 二极管的内部其实就是一个PN结。 把PN结封装起来&#xff0c;两边加上两个电极&#xff0c;就组成了半导体二极管。简称二极管&#xff08;Diode&#xff09; 二极管和PN结一样&#xff0c;具有单向导通性&#xff1a; 外观和正负极 常见芯片封装如下&#xff1a; 一般…

MDETR 论文翻译及理解

题目Abstract1. Introduction2. Method2.1. Background2.2. MDETR2.2.1 Architecture2.2.2 Training 3. Experiments3.1. Pre-training Modulated Detection 预训练调制检测3.2. Downstream Tasks3.2.1 Few-shot transfer for long-tailed detection 4. Related work5. Conclus…

飞行员兄弟

飞行员兄弟 思路&#xff1a; 这里一共有16个格子&#xff0c;如果暴力的话也就是2^16次方种排列组合。 这题和之前的开关不一样&#xff0c;这题是会影响到周围很多格子&#xff0c;而开关那题可以利用上方只改变一个的操作来解题&#xff0c;这题我想到的就是暴搜&#xff…

Prefix-Tuning 论文概述

Prefix-Tuning 论文概述 前缀调优&#xff1a;优化生成的连续提示前言摘要论文十问实验数据集模型实验结论摘要任务泛化性能 前缀调优&#xff1a;优化生成的连续提示 前言 大规模预训练语言模型(PLM)在下游自然语言生成任务中广泛采用fine-tuning的方法进行adaptation。但是f…

Redis 数据结构详解

分类 编程技术 Redis 数据类型分为&#xff1a;字符串类型、散列类型、列表类型、集合类型、有序集合类型。 Redis 这么火&#xff0c;它运行有多块&#xff1f;一台普通的笔记本电脑&#xff0c;可以在1秒钟内完成十万次的读写操作。 原子操作&#xff1a;最小的操作单位&a…

基于Java SSM框架实现实现四六级英语报名系统项目【项目源码+论文说明】

基于java的SSM框架实现四六级英语报名系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个高校四六级报名管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作…

HarmonyOS开发(九):数据管理

1、概述 1.1、功能简介 数据管理为开发者提供数据存储、数据管理能力。 它分为两个部分&#xff1a; 数据存储&#xff1a;提供通用数据持久化能力&#xff0c;根据数据特点&#xff0c;分为用户首选项、键值型数据库和关系型数据库。数据管理&#xff1a;提供高效的数据管…

LeedCode刷题---子数组问题

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、最大子数组和 题目链接&#xff1a;最大子数组和 题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连…

Microsoft Expression Web - 网页布局

在本章中&#xff0c;我们将介绍网页的基本布局。在创建我们的网页布局之前&#xff0c;我们需要考虑我们的内容&#xff0c;然后设计我们希望如何呈现该内容&#xff0c;因为它是在我们的网站上可见的内容。 由我们如何呈现我们的内容&#xff0c;以便我们的观众找到我们的网…

网络运维与网络安全 学习笔记2023.12.3

网络运维与网络安全 学习笔记 第三十三天 今日目标 目录-文件基本管理、vim文本编辑、用户账号管理 组账号管理、归属控制、权限控制 目录-文件基本管理 ls 列目录及文档属性 ls - List 格式:ls[选项]…[目录或文件路径] 1.如果不以/开始,表示相对路径(省略了当前所在位置…

不得不说,HelpLook真的是一个很懂用户的文档管理工具

在当今互联网时代&#xff0c;信息的爆炸性增长使得有效管理和组织文档变得至关重要。随着企业规模的扩大和团队协作的增加&#xff0c;如何高效地存储、共享和访问关键知识和文档成为了一个难题。不过&#xff0c;我早之前有幸发现&#xff0c;HelpLook这个文档工具是真正懂得…

【计算机视觉】基于OpenCV计算机视觉的摄像头测距技术设计与实现

基于计算机视觉的摄像头测距技术 文章目录 基于计算机视觉的摄像头测距技术导读引入技术实现原理技术实现细节Python-opencv实现方案获取目标轮廓步骤 1&#xff1a;图像处理步骤 2&#xff1a;找到轮廓步骤完整代码 计算图像距离前置技术背景与原理步骤 1&#xff1a;定义距离…

【tensorflow学习-选择动作】 学习tensorflow代码调用过程

a actor.choose_action(s) def choose_action(self, s):s s[np.newaxis, :]return self.sess.run(self.action, {self.s: s}) # get probabilities for all actions输入&#xff1a;s 输出&#xff1a;self.sess.run(self.action, {self.s: s}) &#xff1a;a

【云原生Prometheus篇】Prometheus PromQL语句详解 1.0

文章目录 一、前言1.1 Prometheus的时间序列1.1.1 指标名称1.1.2 标签1.1.3 使用的注意事项 1.2 样本数据格式1.3 Prometheus 的聚合函数 二 、PromQL 理论部分2.1 PromQL简介2.2 PromQL的数据类型2.3 时间序列选择器2.3.1 瞬时向量选择器 &#xff08;Instant Vector Selector…

React使用TailwindCSS

React中使用TailwindCSS TailwindCSS是 下载及初始化 可以查看官网对照自己使用的框架进行配置 npm install -D tailwindcss postcss autoprefixer下载完毕后执行如下命令 npx tailwindcss init -p可以发现项目中多了两个文件 其中默认已经进行了配置&#xff0c;我们需要将…

JSP格式化标签 parseNumber指定格式字符串转数字类型

好 我们继续来说格式化标签 parseNumber 它的作用是讲一个字符串 转换为指定格式的数值型 老实说 这东西 作为了解把 实际开发中都不是用得少 我建议还是在java端就处理好 不建议在jsp中高这种类型转换的操作 基本格式如下 这几个属性都是我们这几期jsp标签的老朋友了 我们…

bean依赖属性配置

bean依赖属性配置 文章目录 bean依赖属性配置 Data ConfigurationProperties(prefix "cartoon") public class CartoonProperties {private Cat cat;private Mouse mouse; }cartoon:cat:name: whatage: 5mouse:name: howage: 6这样的话&#xff0c;业务bean无需在读…

使用AOS实现网页动画效果

在现代Web开发中&#xff0c;动画效果是提升用户体验和页面交互性的重要因素之一。而AOS&#xff08;Animate On Scroll&#xff09;作为一个强大的动画库&#xff0c;可以帮助我们轻松地实现网页元素的滚动动画效果。 什么是AOS&#xff1f; AOS是一个基于CSS3和JavaScript的…

[C/C++]数据结构 关于二叉树的OJ题(利用分治思想解决难题)

题目一: 单值二叉树 &#x1f6a9;⛲&#x1f31f;⚡&#x1f966;&#x1f4ac; &#x1f6a9;题目链接:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 ⛲题目描述: 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。…