C语言:qsort详解

news2024/9/25 23:23:13

在上一篇文章我们大致的了解了回调函数的用法和作用,在这一篇让我们来了解一下在回调函数qsort的使用吧。

一.qsort

qsort是一种用来排各种类型数据的函数,利用的是快速排序的方式。说到排序,我们就想到了之前学习的冒泡排序,但冒泡排序也有很明显的缺点:时间复杂度太高,效率慢,但qsort就快很多了,并且还支持各种类型的排序。

qsort的形式

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

  • void* base:待排序数组的第一个元素的地址。
  • size_t num:待排序数组的元素个数。
  • size_t size:待排序数组中一个元素的大小。
  • int (*compar)(const void*, const void*):函数指针指向了一个函数,这个函数是用来比较两个元素的,存放的是需要比较的两个元素的地址。 

大于返回>0的数,小于返回<0的数,等于返回0

二.qsort的使用

1.使用qsort函数排序整型数据 

#include<stdio.h>
#include<string.h>
int cmp_t(const void* e1, const void* e2)
{
	return *(int*)e1 - *((int*)e2);
}
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void test()
{
	int arr[10] = { 3,5,7,9,1,2,8,4,6,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_t);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

因为数组名为首元素地址所以我们第一个只需要将arr传过去,第二个我们用sizeof求出元素个数并赋给sz,第三个是一个元素的大小,所以我们只需要用sizeof求一个元素的大小即可,在最后传一个判断大小的函数名。在进入函数体,我们首先先确定我们的具体需求,我们要的是俩个整数进行比较,但是这里我们拿到的地址是 void* 类型的,所以我们需要进行强制转化,把俩个参数转化为整形,这样才能满足我们的需求。

在判断函数中,因为大于返回>0的数,小于返回<0的数,等于返回0,所以,我们只需要让e1-e2即可,现在我们求出来的是升序,如果我们想要求降序只需要让e2-e1即可。 

判断函数这里更是我们需要注意的地方,我们进入到判断函数中,这个两个参数,都是 void* 的类型,这样设定的目的是为了拿到一个地址,为了泛型编程的思想,我们这里只管拿到地址,不需要思考怎么处理这俩个地址,所以使用 void* 这样就能满足更多的需求。

 2. 使用qsort函数排序字符串

#include<stdio.h>
#include<string.h>
int cmp_t(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}
void print(char* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}
}
void test()
{
	char arr[] ="dcfghta";
	int sz = strlen(arr);
	qsort(arr, sz, sizeof(arr[0]), cmp_t);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

这段代码的运行方式跟上面的其实是一样的,但是对于字符串的比较和求个数是不一样的

所以我们接下来就要讲解一下strlen和strcmp 

1.strlen


我们知道字符串“abcdef"其实是由a b c d e f \0组成的,当我们用sizeof函数是会把\0算在里面,但我们却不需要它,这时候就要用到strlen函数了。

strlen的作用:他是用来计算字符串长度的函数,当他遇到‘\0'的时候就会停止。

需要包含头文件string.h

2.strcmp

strcmp函数是C语言中的一个标准库函数,用于比较两个字符串的大小。它的原型int strcmp(const char *str1, const char *str2);,其中str1和str2是两个要比较的字符串的指针。

大于返回>0的数,小于返回<0的数,等于返回0

好了既然我们已经了解了他们,现在就让我们来看一下结果吧

3.使用qsort排序结构数据 

#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_name(const void* e1, const void* e2)
{
	return strcmp((*(struct S*)e1).name , (*(struct S*)e2).name);
}

void test()
{
	int i = 0;
	struct S s[3] = { {"zhangsan",20},{"lisi",15},{"liliu",18}};
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_name);
	for (i = 0; i < sz; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test();
	return 0;
}

虽然他是结构体类型,但是这段代码本质上比较字符串 ,按照我们的猜想应该长的在后短的在前,但是在我们排序字符串时我们发现字母他是按照循序依次增大的,在多个字符串比较时,他是对应这比较的例如:lisililiu,他是ll,比ii比,sl比,比到这时s大于l,这就导致了,lisi大于liliu.那么让我们看一下是否是这样吧。

既然我们能按照名字来比, 我们肯定也可以来按照年龄来比

#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_age(const void* e1, const void* e2)
{
	return (*(struct S*)e1).age - (*(struct S*)e2).age;
}

void test()
{
	int i = 0;
	struct S s[3] = { {"zhangsan",20},{"lisi",15},{"liliu",18} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_age);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", s[i].age);
	}
}
int main()
{
	test();
	return 0;
}

 三.qsort函数的模拟实现

既然qsort函数这么厉害,我们可不可以用冒泡排序的方法来实现qsort呢?

我们发现qsort的形式

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

如果我们创造一个函数bubble_sort将他的形式按照qsort的模样仿制出来是不是就可以了

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))

1.元素的判断

 在之前我们知道我们传过来的是什么类型的数据,所以我们可以直接进行判断,但现在我们并不知道会传过来什么类型的数据,导致在循环的过程中不知道跳过一个字节所以我们需要改变判断条件,但我们该如何进行改变呢,既然我们不知道他们的类型,但我们可以得到一个元素的大小,我们再用一个元素的大小去乘于j,就能得到跳过几个字节了在加上首元素的字节,同时也得到了元素的地址,只要我们在传到判断函数中就能的到大小了。

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				
			}
		}
	}
}

2.元素的交换

既然我们已经的到了如何判断大小,接下来我们就应该进行交换了。 我们同样不知道会传过来什么类型的数据,但我们可以知道类型字节的大小,我们只要将字节的首地址传过去,在交换每个字节就可以交换两个数据了

void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}

3.完整代码

#include<stdio.h>
int cmp_t(const void* e1, const void* e2)
{
	return *(int*)e1-*(int*)e2;
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	int arr[10] = { 3,5,7,9,1,2,8,4,6,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_t);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

同样的我们也来看一看排字符串

#include<stdio.h>
#include<string.h>
int cmp_t(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	char arr[] = "dcfghta";
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_t);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}
} 
int main()
{
	test();
	return 0;
}

排结构数据(名字)

#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_t(const void* e1, const void* e2)
{
	return strcmp((*(struct S*)e1).name, (*(struct S*)e2).name);
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	struct S s[3]= { {"zhangsan",20},{"lisi",15},{"liliu",18} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_t);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test();
	return 0;
}

排结构数据(年龄)

#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_t(const void* e1, const void* e2)
{
	return (*(struct S*)e1).age- (*(struct S*)e2).age;
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	struct S s[3] = { {"zhangsan",20},{"lisi",15},{"liliu",18} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_t);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", s[i].age);
	}
}
int main()
{
	test();
	return 0;
}

好了今天的分享就到这里吧,还请大家多多关注,我们下一篇见! 

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

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

相关文章

MySQL数据如何高效实时同步到Elasticsearch?【送源码】

概述 在实际的项目开发与运维过程中&#xff0c;MySQL 常常扮演着业务数据库的核心角色&#xff0c;以其强大的事务处理能力和数据完整性保障&#xff0c;支撑着系统的稳定运行。然而&#xff0c;随着数据量的急剧增长和查询复杂度的不断提升&#xff0c;单一依赖 MySQL 进行高…

文章管理接口——里面有动态SQL编写,在分页查询里

1.实体类和表结构 2. 新增文章分类 接口文档 实现 完整代码放在校验部分 结果&#xff1a; 参数校验&#xff08;Validation自定义&#xff09; 对state的校验&#xff08;已发布|草稿&#xff09;&#xff0c;已有的注解不能满足校验需求&#xff0c;这时就需要自定义校验注解…

Day30 | 62.不同路径 63. 不同路径 II 343.整数拆分 96不同的二叉搜索树

语言 Java 62.不同路径 不同路径 题目 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问…

JVM类加载中的双亲委派机制

【1】什么是双亲委派 Java虚拟机对class文件采用的是按需加载的方式&#xff0c;也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时&#xff0c;Java虚拟机采用的是双亲委派模式&#xff0c;即把请求交由父类处理&#xff0c…

【Git企业级开发实战指南①】Git安装、基本操作!

目录 一、Git是什么&#xff1f;1.1特点1.2功能1.3基本概念 二、Git安装2.1Ubuntu下安装2.2Centos下安装Git 三、Git基本操作3.1创建git本地仓库3.2配置Git3.3 工作区&暂存区&版本库3.4 实操案例3.4.1添加文件 3.5 修改文件3.6版本回退3.7查看历史操作日志3.7撤销修改3…

广东省各区县农业产量数据,数据精度至各区县,2020-2023年四年数据可选!

数据名称: 广东省各区县农业产量数据 数据格式: Shpexcel 数据几何类型: 面 数据坐标系: WGS84 数据时间&#xff1a;2020-2023年 数据来源&#xff1a;广东省统计年鉴 数据字段&#xff1a; 序号字段名称字段说明1province省份名称2city城市名称3county区县名称4cou…

【C++深度探索】红黑树的底层实现机制

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;C从入门至进阶 这里将会不定期更新有关C/C的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 前言 红…

VNC未授权访问漏洞

VNC 是虚拟网络控制台Virtual Network Console的英文缩写。它是一款优秀的远程控制工具软件由美国电话电报公司AT&T的欧洲研究实验室开发。VNC是基于 UNXI 和 Linux 的免费开源软件由 VNC Server 和 VNC Viewer 两部分组成。VNC 默认端口号为 5900、5901。VNC 未授权访问漏…

STM32F407移植LVGL(V8.3版本)

一、LVGL简述 1.丰富且强大的模块化图形组件&#xff1a;按钮、图表、列表、滑条、图片等 2.高级图形引擎&#xff1a;动画、抗锯齿、透明度、平滑滚动、图层混合等效果 3.支持多种输入设备&#xff1a;触摸屏、键盘、编码器、按键等 4.配置可裁剪&#xff0c;最低资源占用&am…

latex中Function函数报错

latex写伪码时&#xff0c;发现报错&#xff0c;截图如下&#xff1a; 解决办法&#xff0c;添加宏包&#xff0c;截图如下&#xff1a; \usepackage{algpseudocode}

虚幻引擎 C++ 实现平面阴影

1、平面阴影介绍 平面阴影是一种相对简单的渲染阴影的方式&#xff0c;可以理解为对一个模型渲染两次&#xff0c;一次是渲染模型本身&#xff0c;另一次是渲染模型的投影。渲染投影可以看作是将模型的顶点变换到地面的投影空间再渲染&#xff0c;可以理解为渲染了一个“压扁”…

pytorch学习笔记6 tensor拼接和拆分

cat 合并 dim必须首选相同&#xff08;上例都是3&#xff09;&#xff0c;其次除了合并的dim&#xff08;上例中为dim0&#xff09;外&#xff0c;其它dim的size必须相同&#xff08;dim 1的size是32&#xff0c;dim2的size是8&#xff09;&#xff0c;否则需要手动处理到相同…

vue3 + Spingboot + oracle 通过Base64存储图片

一 、前言 近期在做vue3 Springboot oracle 的工作&#xff0c;有个小功能通过页面导入图片保存到oracle数据库中&#xff0c;本人对前端不是很熟悉&#xff0c;借此记录一下实现方法&#xff1b; 二、前端部分代码 <template><div class"dialog-mian"&…

SQL注入实例(sqli-labs/less-7)

0、初始页面 1、确定闭合字符 确定闭合字符为单引号括号括号 )) ?id1 and 11 ?id1 and 12 ?id1 ?id1)) 2、查看securie_file_priv参数 ?id1)) and upddatexml(1,concat(0x7e,(select secure_file_priv),0x7e),1) -- 3、写入一句话木马 ?id1)) union select null,&q…

SFT、RLHF、DPO、IFT —— LLM 微调的进化之路

TL;DR • SFT、RLHF 和 DPO 都是先估计 LLMs 本身的偏好&#xff0c;再与人类的偏好进行对齐&#xff1b; • SFT 只通过 LLMs 生成的下一个单词进行估计&#xff0c;而 RLHF 和 DPO 通过 LLMs 生成的完整句子进行估计&#xff0c;显然后者的估计会更准确&#xff1b; • 虽然…

壹连科技净利润增速放缓:毛利率清一色下滑,研发费用率远弱同行

《港湾商业观察》施子夫 王璐 从2022年6月20日递表创业板以来&#xff0c;深圳壹连科技股份有限公司&#xff08;以下简称&#xff0c;壹连科技&#xff09;已经走了2年多的历程&#xff0c;如今离挂牌上市近在咫尺。 今年7月22日&#xff0c;壹连科技提交了注册申请。8月2日…

哪个电脑桌面便签好用并且无广告弹窗?

在日常生活和工作中&#xff0c;很多人喜欢在电脑桌面上使用便签软件。便签软件可以方便地记录临时任务、重要信息或者待办事项&#xff0c;帮助用户更好地管理时间和提高工作效率。想象一下&#xff0c;在繁忙的工作中&#xff0c;你能够快速在桌面便签上记下即将要做的任务&a…

基本K8s搭建Jekins+gitee项目自动部署

这里写目录标题 1.基本K8s部署安装Jekins2.设置Jenkins国内镜像源2.安装Gitee插件1.安装Gitee Plugin2.验证安装Gitee Plugin 3.新建任务1.输入任务名称2.输入你gitee上的项目链接3.测试构建 4.查看项目在k8s集群master节点的位置1.确认 Jenkins Pod 名称2.使用kubectl exec到 …

大数据技术复习--概述

概述 数据的概念&#xff1a;数据是指对客观事件进行记录并可以鉴别的符号&#xff0c;是对客观事物的性质、状态以及相互关系等进行记载的物理符号或这些物理符号的组合&#xff0c;是可识别的、抽象的符号。 数据类型&#xff1a;文本、图片、音频、视频 从数据的结构化程…

2024华数杯全国大学生数学建模竞赛B题思路-VLSI电路单元的自动布局-MIA 感知的详细布局问题描述

本章主要对超大规模集成电路&#xff08;Very Large Scale Integration Circuit&#xff0c;VLSI&#xff09;布局 问题进行了描述&#xff0c;首先简单梳理一下超大规模集成电路设计流程、物理设计相关的知 识&#xff0c;接着对 MIA 感知的混合高度单元集成电路详细布局问题的…