C语言——利用冒泡排序模拟实现qsort函数

一.冒泡排序

冒泡排序是C语言中众多排序中的一种。它的排序逻辑为(升序):从第一个元素开始和相邻的比较,如果第一个元素大于第二个元素,则交换,反之不交换;第二个再与第三个元素比较,第二个大于第三个交换,反之不交换……一趟之后,最大的元素将会出现在数组的最后一个;下来再重头开始,继续比较。

我们可以看到,一趟交换,就会将最大的元素放在数组的最后,我们在进行下一趟交换,就可以把次大值放在数组倒数第二个位置。这就是冒泡排序的基本逻辑,下来我们用代码来实现:

//冒泡排序
void bubble_sort(int* nums, int numsSize)
{
	//趟数
	int i = 0;
	for (i = 0; i < numsSize - 1; i++)
	{
		//每一趟需要交换的次数
		int j = 0;
		for (j = 0; j < numsSize - i - 1; j++)
		{
			//前一个大于后一个就进行交换
			if (nums[j] > nums[j + 1])
			{
				int tmp = nums[j + 1];
				nums[j + 1] = nums[j];
				nums[j] = tmp;
			}
		}
	}
}

二.qsort函数

我在上一期博客已经详细的介绍了qsort函数,我们现在来复习一下qsort函数的功能、参数及返回值。

qsort函数的功能就是快速排序一任意类型数组。

而它的参数分别为:待排序数组,数组的大小,数组中每个元素的大小,比较函数。

qsort函数无返回值。

我们知道了qsort函数的功能及参数后,我们就可以在上面写的冒泡排序函数上进行修改,以此达到模拟qsort函数的功能。

三.模拟qsort函数

首先我们是依据冒泡排排序来进行模拟的,而qsort函数本来使用的快速排序。本质上都是排序,我们可以知道,冒泡排序的趟数和每趟交换的次数是不用进行修改的。修改的有:比较两相邻元素的大小、两元素的交换、以及函数参数。

1.函数参数的修改

既然要模仿qsort函数,所以我们应该保持函数参数的一致。

//nums 待排序数组
//numsSize 待排序数组的大小
//width 数组中每个元素的大小
//int(*compare)(const void*, const void*) 比较函数

void bubble_sort(void* nums, size_t numsSize, size_t width, int(*compare)(const void*, const void*))
{

}

2.修改两相邻元素之间的比较方式

因为qsort函数可以比较任意类型的数组,所以传递数组的类型为void*型。而在上一期博客我已经介绍到,两元素之间的比较应该由该函数的使用者来编写,因为使用者肯定知道他们排序的是什么类型的数组。所以,之前冒泡排序中的if语句中的判断条件就应该修改为比较函数对两相邻元素的比较的返回值。比较函数的返回值为整型,>0交换  =0不交换 <0也不交换(默认排序后为升序)。

//比较函数
if (compare((char*)nums + j * width, (char*)nums + (j + 1) * width))
{

}

我们可以先将待比较的两个元素强制转换为char*类型,但是这样的话就会改变原来元素的类型
所以我们再给强转之后的结果乘上数组每个元素的大小即width,这样每个指针访问的时候访问的都是原来数组类型的字节个数
例如:待排序数组为int型,转为char*后,如果直接传给比较函数,就会造成数据的截断,因为整型每个元素占四个字节
我们给其乘上width之后,就相当于指针向后走了width个位置,就相当于指向了下一个整型的位置。
但我们知道,比较的时候,要一直往后走进行比较,所以我们可以给width乘上一个j变量,j = 0,相当与第一个整型,j = 1,相当于跳过了一个width的长度,即一个整型,j = 2的时候,相当于跳过了两个整型。所以,这样就可以比较任意类型的两元素。

3.两元素的交换

之前的冒泡排序只能交换整型,而我们想交换其他类型的时候往往不适用,所以我们可以将元素的交换抽象成一个函数,利用该函数对元素进行交换。

void Swap(char* p1, char* p2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char c = *p1;
		*p1 = *p2;
		*p2 = c;
		p1++;
		p2++;
	}
}

假设我们交换的是整型数据,但传过来的却是字符型,这怎么交换呢?

这幅图就是Swap函数交换的逻辑。我们可以一个字节一个字节的进行交换,因为我们将每个元素的大小传给了Swap函数,所以可以据此利用循环的方式,将其每一个字节都进行交换。

四.利用模拟函数进行整型数据的排序

综合上面的内容,我们就可以写出以冒泡排序为基础的qsort函数的模拟函数。

void bubble_sort(void* nums, size_t numsSize, size_t width, int(*compare)(const void*, const void*))
{
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		int j = 0;
		for (j = 0; j < numsSize - 1 - i; j++)
		{
			
			if (compare((char*)nums + j * width, (char*)nums + (j + 1) * width) > 0)
			{
				Swap((char*)nums + j * width, (char*)nums + (j + 1) * width, width);

			}

		}
	}
}

我们现在利用此函数,进行整型数组的排序:

#include <stdio.h>

//比较函数
int compare_int(const char* p1, const char* p2)
{
	return *(int*)p1 - *(int*)p2;
}

//交换函数
void Swap(char* p1, char* p2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}

//模拟函数
void bubble_sort(void* nums, size_t numsSize, size_t width, int(*compare)(const void*, const void*))
{
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		int j = 0;
		for (j = 0; j < numsSize - 1 - i; j++)
		{
			
			if (compare((char*)nums + j * width, (char*)nums + (j + 1) * width) > 0)
			{
				Swap((char*)nums + j * width, (char*)nums + (j + 1) * width, width);

			}

		}
	}
}


//打印函数
void Print(int* nums, int numsSize)
{
	int i = 0;
	for (i = 0; i < numsSize; i++)
	{
		printf("%d ", nums[i]);
	}
}

void test()
{
	int nums[] = { 10,9,8,7,6,5,4,3,2,1 };
	int numsSize = sizeof(nums) / sizeof(nums[0]);
	bubble_sort(nums, numsSize,sizeof(nums[0]),compare_int);
	Print(nums, numsSize);
}

int main()
{

	//模拟qsort函数
	test();
}

结果为:

五.结语

到此,利用冒泡排序模拟qsort函数已经完成,要想排序不同的类型只需改变比较函数即可(上期博客已介绍,故不在此赘述)。

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

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

相关文章

SAP BAS中Fiori开发的高阶功能(storyboard, navigation, guided development, variant)

1. 前言 在之前的几篇文章中&#xff0c;我介绍了SAP BAS的一些基本功能&#xff0c;包括账户申请&#xff0c;创建工作区&#xff0c;git的使用以及如何step-by-step去创建出你的第一个Fiori项目等等。在本篇中&#xff0c;我将进一步介绍一些在开发Fiori应用程序时会用到的高…

唯众物联网安装调试员实训平台物联网一体化教学实训室项目交付山东技师学院

近日&#xff0c;山东技师学院物联网安装调试员实训平台及物联网一体化教学实训室采购项目已顺利完成交付并投入使用&#xff0c;标志着学院在物联网技术教学与实践应用方面迈出了坚实的一步。 山东技师学院作为国内知名的技师培养摇篮&#xff0c;一直以来致力于为社会培养高…

如何在linux环境上部署单机ES(以8.12.2版本为例)

ES安装&#xff08;以8.12.2版本为例&#xff09; 首先创建好对应的文件夹然后在对应的文件夹下执行依次这些命令 1.wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.12.2-linux-x86_64.tar.gz 2.wget https://artifacts.elastic.co/downloads/…

Android iOS客户端自动化UI自动化airtest从0到1搭建macos

一、基础环境 1. 安装jdk 选择jdk8 如果下载高版本 可能不匹配会失败 下载.dmg文件 苹果电脑 &#xff5c; macOS &#xff5c; jdk1.8 &#xff5c; 环境变量配置_jdk1.8 mac-CSDN博客 Java Downloads | Oracle jdk环境变量配置 找到java home qamac ~ % cd /Library/J…

跳过mysql权限验证来修改密码-GPT纯享版

建议重新配置一遍&#xff0c;弄成功好多次了&#xff0c;每次都出bug&#xff0c;又要重新弄&#xff0c;不是过期就是又登不进去了&#xff0c;我服了 电脑配置MySQL环境&#xff08;详细&#xff09;这个哥们的10min配完&#xff0c;轻轻松松&#xff0c; 旧方法&#xff…

Skywalking的Helm Chart方式部署

背景 之前介绍了AWS云上面的EKS的集中日志方案。这次主要介绍调用链监控了&#xff0c;这里我们用的是Skywalking。监控三王者&#xff08;EFKPrometheusSkywalking&#xff09;之一。之前AWS云上面使用fluent bit替代EFK方案&#xff0c;其实&#xff0c;AWS云在调用链方面&a…

谈谈曲线的阶次

曲线的阶次&#xff08;Degree&#xff09;是数学和几何学中一个重要的概念&#xff0c;它通常与曲线的方程和性质有关。在几何学中&#xff0c;曲线的阶次可以理解为曲线方程的指数或次数。例如&#xff0c;直线的方程是YKxb&#xff0c;它是一次方程&#xff0c;因此直线被认…

PMSM 永磁同步电机滑膜控制 SVPWM矢量控制 matlab simulink 仿真

仿真搭建平台&#xff1a; (1)该模型采用matlab/simulink 2016b版本搭建&#xff0c;使用matlab 2016b及以上版本打开最佳; (2)该模型已经提前转换了各个常用版本&#xff08;最低为matlab2012b&#xff09;&#xff0c;防止出现提示版本过高的情况。 模型截图&#xff1a; 算…

【ReactJS】使用GoJS实现自己的图表App

目录 1:用于绘制自定义图表的JavaScript库:用于绘制UML(或BPMN或ERD …)图表的JavaScript库:2:为什么选择GoJS?3:让我们使用现有的React应用程序:步骤1:步骤2:步骤3:步骤4:推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战1:…

vCenter 6.5为虚拟机添加GPU直通

参考&#xff1a;Dell文档 如何为GPU直通启用VMware虚拟机。 | Dell 中国

VS Code 跳板机登录服务器(手打密码+秘钥登录)

目录 0.为什么要用跳班机登陆服务器&#xff1f; 1.VS Code插件安装及ssh安装 2.密码链接方式 1&#xff09;添加ssh设置&#xff0c;设置主机 2)设置跳板机 Tips:可以直接通过窗口连接文件管理 3.密钥连接方式&#xff08;更安全更方便&#xff09; 1&#xff09;mac版…

机器学习——线性回归(头歌实训)

头歌机器学习实训代码、答案&#xff0c;如果能够帮到您&#xff0c;希望可以点个赞&#xff01;&#xff01;&#xff01; 如果有问题可以csdn私聊或评论&#xff01;&#xff01;&#xff01;感谢您的支持 目录 第1关&#xff1a;简单线性回归与多元线性回归 第2关&#…

Swift 中的 Sequence 是什么 ?

在 Swift 中&#xff0c;Sequence 是一个协议&#xff0c;它表示一个可以遍历其元素的集合类型。任何遵循 Sequence 协议的类型都必须提供一个迭代器&#xff0c;用于按顺序访问其元素。迭代器是通过 makeIterator() 方法获取的&#xff0c;该方法返回一个遵循 IteratorProtoco…

记一次阿里云服务器报错 无法安装Nginx

阿里云服务器。安装Nginx服务器。 报错如下&#xff1a; 这个时候需要修改&#xff1a; ‘etc/yum.conf’ 我们需要把这一行注释掉 这样就可以安装了

3-24游玩计划

总体目标 赏花为主&#xff0c;兼顾山海 平路为主&#xff0c;适当登高 目标1&#xff1a;光明油菜花 参考介绍链接&#xff1a;深圳3月赏花指南&#xff0c;来这片油菜花地追春天吧&#xff01; 地址&#xff1a; pros&#xff1a; 赏油菜花步行安静散心生态采摘拍照打卡…

二、SpringBoot3 配置文件

本章概要 统一配置管理概述属性配置文件使用YAML 配置文件使用批量配置文件注入多环境配置和使用 2.1 统一配置管理概述 SpringBoot工程下&#xff0c;进行统一的配置管理&#xff0c;你想设置的任何参数&#xff08;端口号、项目根路径、数据库连接信息等等)都集中到一个固定…

Linux:文件读取指令

Linux&#xff1a;文件读取指令 cat指令more指令less指令head指令 & tail指令grep指令 cat指令 cat指令用于查看目标文件的内容。 语法&#xff1a;cat [选项][文件] 比如直接使用cat读取一个文件&#xff1a; 可以看到&#xff0c;其直接在指令的下方&#xff0c;输出了t…

echarts图表动态监听dataZoom滑动,控制柱条的宽度以及数值的显示隐藏

当数值过多时&#xff0c;显示所有柱条看着会很凌乱且文字会挤在一起&#xff0c;于是就需要监听datazoom的滑动&#xff0c;拿到对应的阈值后做出相应的配置。 “dataZoom” 事件通常用于响应用户对图表进行数据缩放的操作。 这里是datazoom官网api地址&#xff1a;点击跳转至…

使用Python和OpenFOAM进行流体力学模拟的基础示例

流体力学模拟通常涉及复杂的数学方程和数值方法&#xff0c;例如计算流体动力学(CFD)。OpenFOAM是一个开源的CFD工具箱&#xff0c;它使用C编写&#xff0c;但可以通过Python脚本进行自动化和定制。 以下是一个简单的示例&#xff0c;展示如何使用Python和OpenFOAM进行流体力学…

ios symbolicatecrash 符号化crash

一、准备 1.1 .crash 文件获取 设备连接电脑 打开XCode, 依次 XCode -> Windows -> Device and Simulator -> Open Recent Logs 找到 (对应app名+时间点) -> 右键 Show in Finder 1.2 .dSYM 和 .app 文件获取 .dSYM是十六进制函数地址映射信息的中转文件,调试的…