qsort使用举例和qsort函数的模拟实现

news2024/9/25 19:22:26

qsort使用举例

qsort是C语言中的一个标准库函数,用于对数组或者其他数据结构中的元素进行排序。它的原型如下:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

我们可以去官网搜来看一看:

那么对于其中的参数,下面也有相应的英文解释:

- base:指向要排序的数组或数据结构的第一个元素的指针。
- nmemb:要排序的元素的数量。
- size:每个元素的大小(以字节为单位)。
- compar:指向用于比较两个元素的函数的指针。

compar函数用于比较两个元素的大小关系,返回值为整数,表示两个元素的大小关系。具体返回值的含义如下:
- 若返回值小于0,则表示第一个元素小于第二个元素。
- 若返回值等于0,则表示两个元素相等。
- 若返回值大于0,则表示第一个元素大于第二个元素。

qsort函数根据compar函数的返回值对数组或数据结构中的元素进行排序,可以按照升序或降序进行排序。
那么知道以上这些,下面我们就来使用这个库函数,首先我们以排序整型数组为例:

int arr[10] = { 4,5,7,6,3,2,8,9,1,10 };

假设我们要排列以上的10个元素,那么我们根据上面对于库函数qsort的认识,需要向它传入四个参数,那么第一个为- base:指向要排序的数组或数据结构的第一个元素的指针,那么就是我们的arr 了,第二个为- nmemb:要排序的元素的数量,这是我们就要计算里面有多少个元素了,我们可以这样算:

int sz = sizeof(arr) / sizeof(arr[0]);

使用第二个参数我们可以写为  sz ,第三个为- size:每个元素的大小,那么我们计算出数组中第一个元素有多少个字节就可以了 sizeof(arr[0]),最后一个为- compar:指向用于比较两个元素的函数的指针,这里我们就需要我们自己写一个函数了,告诉这个库函数qsort我们要比较什么类型的数据:

qsort(arr, sz, sizeof(arr[0]), cop_int);

那么接下来就是这个cop_int函数怎么写了,因为我们不知道会传什么样的数据,所以我们可以使用void*进行接收,返回类型为int型,因为下面是根据大于或小于或等于来排序的,而且不希望改我们所要排序的数值,所以我们这样写到:

int cop_int(const void* p1, const void* p2)

上面传来了void*类型的数据,而void*的数据不能够直接进行运算操作,所以下面我们要进行强制类型转换,作差看看两数的情况:

return *(int*)p1 - *(int*)p2;

下面就来看看我们整体的代码:

#include <stdlib.h>//头文件

void Print_arr(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int cop_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

int main()
{
	int arr[10] = { 4,5,7,6,3,2,8,9,1,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cop_int);
	Print_arr(arr, sz);

	return 0;
}

看看我们的运行效果:

 下面我们给结构体进行排序:

我们先随便写一个结构体:

struct Stu
{
	char name[20];
	int age;
	int score;
};//定义一个学生的名字,岁数,分数

我们只需要改我们的- compar:指向用于比较两个元素的函数的指针,我们先以学生的名字进行排序,那么结构体我们只需要转换成结构体指针就可以进行比较了,名字我们strcmp进行比较:

#include <stdlib.h>//qsort的头文件
#include <string.h>//strcmp的头文件
struct Stu
{
	char name[20];
	int age;
	int score;
};
int cop_Stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name , ((struct Stu*)p2)->name);
}
int main()
{
	struct Stu arr[] = { {"zhangsan",18,76},{"lisi",28,65},{"wangmazi",25,79}};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cop_Stu_by_name);
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d %d\n", arr[i].name, arr[i].age,arr[i].score);
	}
	return 0;
}

看看运行的结果:

 是正确的哦!我们还可以以年龄进行比较还有分数啊,这里我就不排了,我们下面用冒泡排序来qsort函数的模拟实现!

qsort函数的模拟实现(采用冒泡排序法)

对于冒泡排序,我在前面也写到过,还不太清楚的小伙伴可以看看哦。

void BubbleSort(int arr[], int sz)
{

	for (int i = 0; i < sz - 1; i++)//趟数
	{

		for (int j = 0; j < sz - 1 - i; j++)//一趟冒泡排序
		{

			if (arr[j] > arr[j + 1])//需要进行改造的地方 1
			{
				int tmp = arr[j];// 需要进行改造的地方 2
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;

			}
		}

	}
}

int main()
{
	int arr[] = { 5,7,9,4,3,6,8,1 };//5 7 9 4 3 6 8 1
	int sz = sizeof(arr) / sizeof(arr[0]);//计算有多少个元素
	BubbleSort(arr, sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}


	return 0;
}

首先我们先来想一想,那些地方需要我们改造,对于循环那些我们是不是就不用改了,那不会有影响。我们将会排序到不同的数据类型,所以我们对于 if (arr[j] > arr[j + 1])的比较,是不是需要我们进行改造呢,还有下面的交换变量,也是我们需要改的,既然上面的类型都被改了,下面也肯定是需要改的。

我们以结构体的年龄为例,那么函数的参数还是我们上面的那样,只是要改一下第四个参数:

bubble_sort2(arr, sz, sizeof(arr[0]), cop_Stu_by_age);

接下来就是对于bubble_sort2函数的定义了,下面传了四个参数,那我们也要用四个参数进行接收了,因为我们不知道传来的是什么类型的,所以在设置类型上,我们要考虑考虑,第一个为地址,我们用void*进行接受吧,void*都装的了,哈哈。第二个和第三个就用size_t了,最后一个像我们上面考虑的那个样:int (*cmp)(const void* p1, const void* p2)。

void bubble_sort2(void* base,size_t sz,size_t width, int (*cmp)(const void* p1, const void* p2))
{
	for (int i = 0; i < sz-1; i++)
	{
		for (int j = 0; j < sz-1-i; j++)
		{
			if (arr[j] > arr[j + 1])
			
			{
				
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

接着就是对于比较的改造了,我们这里构造一个新的函数进行比较,这样我们根据下面传来的类型去调用相应的,那么我们怎样找到我们的对应的元素进行比较呢?下面给给了我们首元素的地址,和每个元素的大小,我们是不是可以用起来呢,我们根据j的变化去找到元素。但是还要想到我们要用它来不同数据类型的排序,所以我们也要进行强制类型转换。我们强制转换成什么类型的数据呢?是不是char*最为合适呢,不管是什么类型,我们都可以一个一个的去进行交换:

if(cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)

下面我们进行对于交换的改造:上面改成了那样,所以接收时也改成相应的就行了,我们以前进行交换的时候是创建了第三个变量进行交换,现在我们不知什么类型,就需要改进了。强制转换成什么类型比较合适呢?假设我们结构体的为7个字节大小,那我们是不是用char类型就可以了,不管你如何,我一个一个的进行交换就都可以实现,既然我们以这样的方式进行实现,那我们是不是应该把它每个元素的大小传进来呢:

void swap(char* a, char* b, size_t width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *a;
		*a = *b;
		*b = tmp;
		a++;
		b++;
	}
}

接下来我们就看看整体的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Stu
{
	char name[20];
	int age;
};

int cop_Stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

int cop_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

void PrintArr(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void swap(char* a, char* b, size_t width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *a;
		*a = *b;
		*b = tmp;
		a++;
		b++;
	}
}

//冒泡排序
void bubble_sort2(void* base,size_t sz,size_t width, int (*cmp)(const void* p1, const void* p2))
{
	for (int i = 0; i < sz-1; i++)
	{
		for (int j = 0; j < sz-1-i; j++)
		{
			//if (arr[j] > arr[j + 1])
			if(cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
				/*int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;*/
			}
		}
	}
}

int main()
{

	struct Stu arr[] = { {"aihua",18},{"zhanghong",21},{"kiki",15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//年龄
	bubble_sort2(arr, sz, sizeof(arr[0]), cop_Stu_by_age);
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d\n", arr[i].name, arr[i].age);
	}
	return 0;
}

看看运行结果:

 那么也是可以的哟,我这里就是用其他类型进行排序了,感兴趣的话,可以去试试哦,本来还想着画图给你们梳理一下,可那个画图工具不给力😢😢😢!

愿你们冬不寒!❤❤❤

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

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

相关文章

如何在本地搭建Oracle数据库实现公网环境下通过PLSQL工具进行远程访问

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

C语言 字符函数汇总,模拟实现各字符函数(炒鸡详细)

目录 求字符串长度 strlen 示例 模拟实现strlen 长度不受限制的字符串函数 strcpy 示例 模拟实现strcpy strcat 模拟实现strcat strcmp 示例 模拟实现strcmp 长度受限制的字符串函数介绍 strncpy 示例 模拟实现strncpy strncat 示例 模拟实现strncat s…

hypermesh常用快捷键

#hypermesh常用快捷键

YOLOv8 加持 MobileNetv3,目标检测新篇章

🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv8:我的专业笔记与技术总结   -- YOLOv8轻松上手, 适用技术小白,文章代码齐全,仅需 …

【Dubbo】Dubbo负载均衡实现解析

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

Java多线程(3)

Java多线程(3) 深入剖析Java线程的生命周期&#xff0c;探秘JVM的线程状态&#xff01; 线程的生命周期 Java 线程的生命周期主要包括五个阶段&#xff1a;新建、就绪、运行、阻塞和销毁。 **新建&#xff08;New&#xff09;&#xff1a;**线程对象通过 new 关键字创建&…

Network(五)数值介绍与子网划分

一 数值 1 数值介绍 &#xff08;1&#xff09;带宽 在一定时间内通过某一网络连接的信息量 基本单位&#xff1a;比特每秒 (bit/s) 在计算机软件方面用字节每秒为单位 &#xff08;2&#xff09;存储量 计算机存储量可以用位和字节计量 &#xff08;3&#xff09;常用…

【Linux】-进程间通信-命名管道文件(没有关系的进程间进行通信),以及写一个日志模板

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

【设计模式】聊聊模板模式

原理和实现 设计模式的原理和实现是比较简单的&#xff0c;难的是掌握具体的应用场景和解决什么问题。而模板模式是为来解决复用和拓展两个问题。 模板模式在一个方法中定义好一个算法框架&#xff0c;然后将某些步骤推迟到子类中实现&#xff0c;子类可以在不修改父类流程的时…

【C++】泛型编程 ⑦ ( 模板类常用用法 | 模板类声明 | 模板类调用 | 模板类作为函数参数 )

文章目录 一、类模板基础用法1、类模板声明定义2、类模板使用3、类模板做函数参数 二、完整代码示例1、代码示例2、执行结果 一、类模板基础用法 1、类模板声明定义 上一篇博客中 , 【C】泛型编程 ⑥ ( 类模板 | 类模板语法 | 代码示例 ) 讲解了模板类的基础语法 , 模板类声明如…

DrugMAP: molecular atlas and pharma-information of all drugs学习

DrugMAP&#xff1a;所有药物的分子图谱和制药信息 - PMC (nih.gov) DrugMAP: the molecular atlas and pharma-information of drugs (idrblab.net) 构建了一个描述药物分子图谱和药物信息的新数据库&#xff08;DrugMAP&#xff09;。它提供了>30 000种药物/候选药物的相…

安装Nginx时报:./configure: error: can not define uint32_t

安装版本&#xff1a;v.1.25.3 安装命令&#xff1a; ./configure --prefix/opt/nginx/1.25.3 && make && make install 执行命令后出现异常&#xff0c;如图&#xff1a; 解决方法&#xff1a; yum install -y kernel-headers kernel-devel gcc make

闭眼检测实现

引言 这段代码是一个实时眼睛状态监测程序&#xff0c;可以用于监测摄像头捕获的人脸图像中的眼睛状态&#xff0c;判断眼睛是否闭合。具体应用实现作用说明如下&#xff1a; 1. 实时监测眼睛状态 通过摄像头捕获的实时视频流&#xff0c;检测人脸关键点并计算眼睛的 EAR&a…

【C++】基础语法(中)

C基础语法&#xff08;中&#xff09; 文章目录 C基础语法&#xff08;中&#xff09;01数组一维数组数组初始化注意访问练习1练习2练习3普通做法&#xff1a;优化reverse函数练习4 多维数组清空数组memsetmemcpy 数组的部分由上到下&#xff0c;按规律 蛇形矩阵技巧 02 字符串…

UE 视差材质 学习笔记

视差材质节点&#xff1a; 第一个是高度图&#xff0c; Heightmap Channel就是高度图的灰色通道&#xff0c;在RGBA哪个上面&#xff0c;例如在R上就连接(1,0,0,0)&#xff0c;G上就连接&#xff08;0,1,0,0&#xff09;逐次类推 去看看对比效果&#xff1a; 这个是有视差效果…

【心得】PHP文件包含基本利用个人笔记

本文可能比较凌乱&#xff0c;快速总结保证自己看得懂&#xff08;真.个人笔记&#xff09; 文件包含的本质&#xff1a;代码复用、并行开发、模块化、增加移植性 include和eval的区别&#xff1a; include和eval一样&#xff0c;都不是函数&#xff0c;都是语言结构&#xf…

2024全网最新最全的Pytest接口自动化测试框架教程

pytest编写的规则&#xff1a; 1、测试文件以test_开头&#xff08;以_test结尾也可以&#xff09; 2、测试类以Test开头&#xff0c;并且不能带有__init__方法 3、测试函数以test_开头 4、断言必须使用assert pytest.main([-s,-v]) &#xff1a;用来执行测试用例 -s 打印prin…

CnosDB有主复制演进历程

分布式存储系统的复杂性涉及数据容灾备份、一致性、高并发请求和大容量存储等问题。本文结合CnosDB在分布式环境下的演化历程&#xff0c;分享如何将分布式理论应用于实际生产&#xff0c;以及不同实现方式的优缺点和应用场景。 分布式系统架构模式 分布式存储系统下按照数据复…

Mybatis-Plus《学习笔记 22版尚硅谷 》——感谢【尚硅谷】官方文档

Mybatis-Plus《学习笔记 22版尚硅谷 》 一、MyBatis-Plus1.简介2.特性3.支持数据库4.框架结构5.官方地址 二、入门案例1.开发环境2.建库建表3.创建工程4.配置编码5.测试查询 三、增删改查1.BaseMapper<T>2.调用Mapper层实现CRUD2.1 插入2.2 删除a、根据ID删除数据b、根据…