C语言--- qsort函数

目录

一.qsort函数

1.qsort函数的功能

2.四个参数讲解

(1)base

(2)num

(3)size

(4)compare

 3.使用qsort函数对一个整形数组进行排序

4.qsort函数排序结构体数据 

第一种:按照年龄进行比较

 第二种:按照名字进行排序

二.利用冒泡排序模仿qsort函数

回顾:冒泡排序

 代码实现:


一.qsort函数

void qsort(void* base, size_t num, size_t size,int (*compar)(const void*, const void*));

1.qsort函数的功能

qsort函数是用来进行快速排序的。

那么它的四个参数分别是什么呢? 

2.四个参数讲解

参考网址:qsort - C++ Reference

(1)base

 base是一个指针,它所指向的是被排序数组的第一个元素。

void*通常被我们叫做空指针,也叫泛型指针,因为不知道数组中元素的类型是什么,所以只能先将首元素的地址转化为一个空指针。

(2)num

num指的是数组元素的个数。

size_t是 unsigned int 类型.

(3)size

size是数组中元素的大小,单位是字节。

(4)compare

compare是什么呢?compare的意思是比较,对数组中的元素进行排序,必然离不开比较。

compare就是一个函数指针,它所指向的这个函数的返回类型是int,有两个参数,类型都是const void*.这个函数会被反复调用来进行两个元素的比较。

假设这两个参数变量名分别是p1和p2。

这个函数的返回值,取决于所指向的对象的大小的。这个函数是需要我们自行完成的。

如果p1所指向的元素小于p2所指向的元素,那么返回值小于0

如果p1所指向的元素大于p2所指向的元素,那么返回值大于0

如果p1所指向的元素等于p2所指向的元素,那么返回值等于0

 3.使用qsort函数对一个整形数组进行排序

qsort函数的头文件是<stdlib.h> 

代码:

#include<stdio.h>
#include<stdlib.h>
int cmp_int(const void* p1,const void*p2) {
	
}
void print(int *p,int sz)
{
	for (int i = 0; i < sz;i++) {
		printf("%d ",p[i]);
	}
}
int main()
{
	int arr[] = {1,3,5,7,9,2,4,6,8,0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz,sizeof(int),cmp_int);//排序
	print(arr,sz);//打印
	
	return 0;
}

那么这个cmp_int函数怎么实现呢。

我们知道解引用操作符是不能解引用void*类型的指针的。所以我们需要先将p1和p2进行强制类型转换,再来比较大小。

int cmp_int(const void* p1,const void*p2) {
	if (*(int*)p1 < *(int*)p2){
		return -1;
	}
	else if (*(int*)p1 > *(int*)p2){
		return 1;
	}
	else {
		return 0;
	}
}

但是这样写其实有点复杂,因为qsort函数不管返回值是多少,而是返回值到底是正还是负,所以我们可以对cmp_int函数进行简化。

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

输出结果: 

这样我们就完成了这个整形数组的排序,继续仔细观察的话,我们发现这个是升序排列,如果我们改变一下返回值的比较方式,我们就可以实现降序排列。

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

输出结果:

4.qsort函数排序结构体数据 

排序结构体时,我们只能针对结构体中的一种数据进行排序。 

假设有一个用于描述学生的结构体,有两个结构体成员,分别是名字和年龄

第一种:按照年龄进行比较

#include<stdio.h>
#include<stdlib.h>
struct stu
{
	char name[20];//名字
	int age;//年龄
};
int cmp_age(const void*p1,const void*p2) {
	return  ((struct stu*)p1)->age - ((struct stu*)p2)->age;
//因为p1和p2的类型是const void*,所以我们还是要强制类型转化为结构体指针。
}
int main()
{
	struct stu arr[3] = { {"zhangsan",38} ,{"lisi",28} ,{"wangwu",18} };//结构体数组
	int sz = sizeof(arr) / sizeof(arr[0]);
	//如何进行排序?
	//第一种:利用年龄进行排序
	qsort(arr,sz,sizeof(struct stu),cmp_age);
	return 0;
}

 通过调试,我们在监视窗口中可以看到数组的第一个元素(结构体)变成了王五,18岁。不再是张三,38岁。

 第二种:按照名字进行排序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{
	char name[20];//名字
	int age;//年龄
};
int cmp_age(const void*p1,const void*p2) {
	return  ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}
int cmp_name(const void*p1,const void*p2) {
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
int main()
{
	struct stu arr[3] = { {"zhangsan",38} ,{"lisi",28} ,{"wangwu",18} };//结构体数组
	int sz = sizeof(arr) / sizeof(arr[0]);
	//如何进行排序?
	//第一种:利用年龄进行排序
	//qsort(arr,sz,sizeof(struct stu),cmp_age);
	//第二种:利用名字进行排序
	qsort(arr,sz,sizeof(struct stu),cmp_name);
	return 0;
}

这里我们还需要用到strcmp函数的,strcmp函数的两个参数是字符串,这个函数用于比较字符串,但是不是利用字符串的长度进行比较而是ASCII值进行比较。

如果前一个字符串大于后一个字符串,返回值大于0,反之小于0,如果相等就返回0,这和qsort函数中的compare所指向的函数返回值规则一致。

例如:"abc"和"abcd"这两个字符串肯定就是第二个更大。

但是"abcd"和"abe"就是第二个大了,因为这个函数不会利用整个字符串的ASCII值之和来比较,而是一个字符一个字符进行比较,"abcd"和"abe"的前两个字符都一样,所以会看第三个字符,c的ASCII值明显小于e,所以后者更大,就不会管第四个字符了。

通过调试的监视窗口,结果是:

 之所以这样排序,是因为ASCII值 l < w < z.

二.利用冒泡排序模仿qsort函数

qsort函数实际上使用的是快速排序。

回顾:冒泡排序

冒泡排序的核心思想:两个相邻元素进行比较 ;

如果存在十个数无序排列,比如: 2,3,1, 4,10,7, 5,6, 8,9;

如果我们要将重新排列,变成左边小右边大的情况,我们就可以用到冒泡排序。如何进行冒泡排序呢? 

首先我们先比较第一个数和第二个数,如果第一个数大于第二个数就交换位置,反之就不交换,然后我们再比较第二数和第三个数,同理如果第二个数更大就交换,就这样一直进行下去,直到完成第九个数和第十个数。就这样我们完成了一轮交换。

读者可以自行用上面的数进行尝试,我们会发现最大的那个数经过了一轮交换后(因为最大的数一定比它右边的数大,所以它会一直交换下去,直到最后一个位置),就到了最后一个位置,但其他的数还是乱的,所以我们要接着进行下一轮交换,但是因为最大的数已经在它该在的位置上了,这样在下一轮的交换中我们就不需要比较第九个数和第十个数了;同理后面也是如此…………。

第一轮中我们比较了9次,第二轮比较8次,第三轮比较7次,总共需要比较多少轮呢?

答案是9轮。第九轮的时候比较一次,也就是第一个位置上的数和第二个位置上的数进行比较,第二个位置上的数确定了后,同理第一个位置上的数也应该是最小的了,所以就不用比较了。

原文链接:http://t.csdnimg.cn/VWTYVicon-default.png?t=N7T8http://t.csdnimg.cn/VWTYV

 代码实现:

qsort函数的优点就是可以对任意类型的数据进行排序。模仿qsort函数我们仍然使用相同的变量

 这里我们采用升序。

void bubble_qsort(void* base,size_t num,size_t size,int(*compare)(const void*,const void*)) {
//这里的sz指的是数组的元素个数
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

这里面是冒泡排序的代码,我们如何改变才能变得和qsort函数一样对任意函数进行排序。

首先if的判断条件需要更改,因为我们在compare所指向的函数中进行了比较了,当compare的返回值大于0时就需要交换元素。

那么if括号中应该怎么写呢?,首先我们要明白在比较的两个元素分别是数组的第(j)个元素和数组的第(j+1)个元素,也就是说我们需要找到这两个元素的地址。

base是数组首元素的地址,我们知道指针+-整数会根据指针的类型,跳过不同的字节,这样我们就可以找到其他元素的地址,但是base的类型是void*,这样我们就不能进行+-整数,所以我们需要先进行强制类型转化,那么转化为什么类型的指针呢?

答案是char*,为什么呢?

这里就跟我们没有使用的元素大小size有关了,char*类型的指针,在+-1的时候只会跳过一个字节,更方便我们使用,如果是int*而数组元素的大小是7个字节,无论如何我们也不能得到其他元素的地址了,因为跳过的字节数是4的倍数,永远不可能是7.

那么如何得到第j个元素的地址?

(char*)base+j*size,这就是第j个元素的地址。

所以if括号中应该这样写

if (compare((char*)base + j * size, (char*)base + (j + 1) * size) > 0) 

然后就是如何进行元素的交换了。

为了代码的可读性,我们再写一个swap函数进行元素的交换。

//p1 = (char*)base + j * size, p2 = (char*)base + (j + 1) * size
void _swap(void *p1, void * p2, int size)

因为我们不知道元素的类型,我们是无法进行解引用的,那么如何解决这个问题呢?

同理我们可以先将其强制类型转化为char*的,但是char*的指针在解引用时,只会得到一个字节的数据,万一我们的元素大小是大于1的呢?

我们就可以一个字节一个字节的交换,这样又用到了数组的元素大小。

void swap(void* p1, void* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}

所有代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp_int(const void* p1,const void*p2) {
	return *(int*)p1 - *(int*)p2;
}
void print(int *p,int sz)
{
	for (int i = 0; i < sz;i++) {
		printf("%d ",p[i]);
	}
}
struct stu
{
	char name[20];//名字
	int age;//年龄
};
int cmp_age(const void*p1,const void*p2) {
	return  ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}
int cmp_name(const void*p1,const void*p2) {
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
void swap(void* p1, void* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void bubble_qsort(void* base,size_t num,size_t size,int(*compare)(const void*,const void*)) {
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - i - 1; j++)
		{
			if (compare((char*)base + j * size, (char*)base + (j + 1) * size) > 0) 
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
			}
		}
	}
}


int main()
{
	struct stu arr1[3] = { {"zhangsan",38} ,{"lisi",28} ,{"wangwu",18} };
	int sz1 = sizeof(arr1) / sizeof(arr1[0]);
	//qsort(arr,sz,sizeof(struct stu),cmp_age);
	bubble_qsort(arr1,sz1,sizeof(struct stu),cmp_name);

	int arr2[] = { 1,3,5,7,9,2,4,6,8,0 };
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);
	bubble_qsort(arr2, sz2, sizeof(int), cmp_int);//排序
	print(arr2, sz2);//打印
	return 0;
}

输出结果:

 

 

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

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

相关文章

慢sql优化记录1

慢sql为&#xff1a; select count(*) from t_wf_process p left join t_wf_core_dofile dofile on p.wf_instance_uid dofile.instanceid join zwkj_department d on p.userdeptid d.department_guid ,t_wf_core_item i,wf_node n where (p.IS_DUPLICATE ! true or p.IS_DU…

Leetcoder Day39| 动态规划part06 完全背包问题

完全背包理论 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 示例&#xff1a; 背包最大…

2023第二届陇剑杯网络安全大赛 SS Writeup

sevrer save_1 打开流量包文件过滤http流量 从这个/helloworld/greeting开始追踪TCP流 直接百度搜索payload 搜索得到这题flag就是CVE-2022-22965 sevrer save_2 追踪TCP流&#xff0c;在tcp.stream eq 106&#xff0c;发现反弹shell的IP和端口 这题flag为192.168.43.128:2333…

PPT模板一键下载,原创精美,2024必备!

1. PPT模板分享 &#xff08;1&#xff09;计算机学院毕业答辩PPT &#xff08;2&#xff09;开学典礼活动策划方案PPT &#xff08;3&#xff09;新员工入职培训PPT &#xff08;4&#xff09;宠物行业分析报告PPT &#xff08;5&#xff09;机关青年干部述职PPT 以上PPT模板均…

centos离线安装 k8s (实操可用)

全部安装包rpm下载&#xff08;已整理好k8s和docker&#xff09;&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1ATv8BPijhvIKWz4hMnkx6Q?pwdt5db 提取码&#xff1a;t5db 将文件下载以后&#xff0c;解压到服务器 #执行所有docker-rpm包 yum -y localinstall *.rpm…

testvue-个人中心

header.vue(右上角) <template><div class="header"><!-- 折叠按钮 --><div class="collapse-btn" @click="collapseChage"><i v-if="!collapse" class="el-icon-s-fold"></i><…

实现大华摄像头的抓图-使用HTTP方式

实现抓图&#xff0c;网上大部分都是使用SDK二次开发的&#xff0c;HTTP接口实现的基本没有介绍&#xff0c;好像官方叫CUI接口&#xff0c;但是找官方要文档&#xff0c;基本要不到&#xff0c;我自己下载了一份以前的文档&#xff0c;可以做大部分操作&#xff0c;这里免费分…

【MySQL】用户管理 -- 详解

如果我们只能使用 root 用户&#xff0c;这样存在安全隐患。这时就需要使用 MySQL 的用户管理。 一、 用户 1、用户信息 MySQL 中的用户都存储在系统数据库 MySQL 的 user 表中。 字段解释&#xff1a; host&#xff1a;表示这个用户可以从哪个主机登陆&#xff0c;如果…

哪些公司在招聘GIS开发?为什么?

之前我们给大家整理汇总了WebGIS在招岗位的一些特点&#xff0c;包括行业、学历、工作经验等。WebGIS招聘原来看重这个&#xff01;整理了1300多份岗位得出来的干货&#xff01; 很多同学好奇&#xff0c;这些招GIS开发的都是哪些公司&#xff1f;主要是做什么的&#xff1f; …

gym平衡木训练Q-learning完整代码

安装 pip install gym编码运行 #codingutf8import gym import numpy as npenv gym.make(CartPole-v0)max_number_of_steps 200 # 每一场游戏的最高得分 #---------获胜的条件是最近100场平均得分高于195------------- goal_average_steps 195 num_consecutive_iteration…

Pytorch入门实战 P1-实现手写数字识别

目录 一、前期准备&#xff08;环境数据&#xff09; 1、首先查看我们电脑的配置&#xff1b; 2、使用datasets导入MNIST数据集 3、使用dataloader加载数据集 4、数据可视化 二、构建简单的CNN网络 三、训练模型 1、设置超参数 2、编写训练函数 3、编写测试函数 4、…

双碳目标下DNDC模型建模方法及在土壤碳储量、温室气体排放、农田减排、土地变化、气候变化中的应用

由于全球变暖、大气中温室气体浓度逐年增加等问题的出现&#xff0c;“双碳”行动特别是碳中和已经在世界范围形成广泛影响。国家领导人在多次重要会议上讲到&#xff0c;要把“双碳”纳入经济社会发展和生态文明建设整体布局。同时&#xff0c;提到要把减污降碳协同增效作为促…

浅析extern关键字

C中extern关键字的使用 文章目录 C中extern关键字的使用前言正文1. C与C编译区别2. C调用C函数3. C中调用C函数 总结 前言 ​ C 是一种支持多范式的编程语言&#xff0c;它既可以实现面向对象的编程&#xff0c;也可以实现泛型编程和函数式编程。C 还具有与C语言的兼容性&…

大数据最佳实践

本文主要收录一些大数据不错的实践文章 1、数禾云上数据湖最佳实践 https://blog.51cto.com/u_15089766/2601706 该文章介绍了数禾云的数据胡实践&#xff0c;包含presto以及数据湖等组件的一些部署架构&#xff0c;文章听不错的&#xff0c;里面提到了为了避免presto与yarn计…

【Kaggle】练习赛《肥胖风险的多类别预测》

前言 作为机器学习的初学者&#xff0c;Kaggle提供了一个很好的练习和学习平台&#xff0c;其中有一个栏目《PLAYGROUND》&#xff0c;可以理解为游乐场系列赛&#xff0c;提供有趣、平易近人的数据集&#xff0c;以练习他们的机器学习技能&#xff0c;并每个月都会有一场比赛…

【开源】SpringBoot框架开发快乐贩卖馆管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 搞笑视频模块2.3 视频收藏模块2.4 视频评分模块2.5 视频交易模块2.6 视频好友模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 搞笑视频表3.2.2 视频收藏表3.2.3 视频评分表3.2.4 视频交易表 四、系…

【剑指offer--C/C++】JZ6 从尾到头打印链表

一、题目 二、本人思路及代码 直接在链表里进行翻转不太方便操作&#xff0c;但是数组就可以通过下标进行操作&#xff0c;于是&#xff0c; 思路1、 先遍历链表&#xff0c;以此存到vector中&#xff0c;然后再从后往前遍历这vector,存入到一个新的vector&#xff0c;就完成…

OPC UA 学习笔记:状态机/有限状态机

有限状态机 有限状态机 &#xff08;FSM&#xff09; 是程序员、数学家、工程师和其他专业人士用来描述具有有限数量条件状态的系统的数学模型。 有限状态机的构成包括以下内容&#xff1a; 一组潜在的输入事件。与潜在输入事件相对应的一组可能的输出事件。系统可以显示的一…

dubbo3适配springboot2.7.3

版本详细 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.3</version> </dependency><parent><groupId>org.springframework.boot</groupId><artifactId&…

13年测试老鸟,接口性能测试-压测总结汇总,一文概全...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、概述 性能测试…