qsort函数

news2024/11/15 2:14:01

目录

  • 1.什么是qsort函数
  • 2.实现一个qsort函数
  • 3.用qsort函数排序一个结构体
  • 4.模仿qsort的功能实现一个通用的冒泡排序

1.什么是qsort函数

我们以前学习过的一些排序算法,如冒泡、希尔、快排等等,它们速度有快有满,但是这些排序都只能排序一种类型的变量,如果想排序另一种变量就需要另写一个排序, 那么有没有什么排序是“万能的”呢,什么类型数据都能排的呢?

答案就是qsort函数

qsort函数实现了一种快速排序算法,对一个由n个元素组成的数组进行排序,每个元素的宽度为字节。参数base是一个指向要排序的数组基数的指针,qsort用排序后的元素覆盖这个数组。参数compare是一个指向用户提供的例程的指针,用于比较两个数组元素并返回一个指定它们之间关系的值。

这是qsort函数的官方定义:
在这里插入图片描述

这个函数有四个参数

  • 第一个参数base是待排数组的起始地址
  • 第二个参数num是数组的元素个数,也就是数组的大小
  • 第三个参数width是一个元素的大小,单位是字节,也就是一个元素所占大小
  • 第四个参数compare是一个函数指针,这个参数较为复杂,接下来我们展开讲

在排序中,比较整形或比较浮点型可以用大于,小于,等于;比较两个字符串可以用strcmp函数;但是如果比较两个结构体怎么比较,按照结构体中哪个元素进行比较呢?所以不同类型的元素应用不同的方法去比较

这也就是compare这个函数干的事,在这个函数里,我们自己写两个元素应该怎么比较
compare这个函数指针传回qsort函数,qsort就会按照compare函数中比较的方法,对数组中元素进行比较

compare函数中,elem1指的是要比较的两个元素中第一个元素的地址,elem2是另一个要比较的元素的地址,因为这个函数官方在定义的时候,并不知道要比较什么类型的元素,所以用void*类型.

compare函数是有返回值的:
在这里插入图片描述

  • elem1小于elem2,返回负数
  • elem1大于elem2,返回正数
  • elem1等于elem2,返回0

按照comparer函数的定义,数组以递增的顺序进行排序。要按递减顺序对数组进行排序,颠倒comparer函数中 "大于 "和 "小于 "的含义。


2.实现一个qsort函数

有一个存放int类型变量的数组arr

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

然后使用qsort函数

  • 第一个参数是数组名arr
  • 第二个参数是数组长度int size = sizeof(arr) / sizeof(arr[0]);
  • 第三个参数是一个元素的大小sizeof(arr[0])
  • 第四个参数是函数指针cmp_int

cmp_int函数中,因为传的参数是void*类型,并且待排数组是int类型的,所以在比较函数中,需要将void*类型的变量强制转换成int*类型的指针再进行解引用

如果是排升序,就按照cmp_int函数中参数的顺序将两个指针解引用后相减,否则就颠倒两个指针的顺序

代码如下:

#include <stdio.h>

int cmp_int(const void* e1, const void* e2)
{
	//排升序
	return *(int*)e1 - *(int*)e2;

	//排降序
	//return *(int*)e2 - *(int*)e1;
}

int main()
{
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	int size = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, size, sizeof(arr[0]), cmp_int);
	
	for (int i = 0; i < size; i++)
	{
		printf("%d ", arr[i]);

	}
}

3.用qsort函数排序一个结构体

下面我们用qsort排序一个结构体
结构体如下:

struct stu
{
	int age;
	char name[10];
};

然后使用qsort函数,结构体中有整形和字符串两个元素,这两个元素都可以进行比较和排序

首先我们按照年龄来排序,比较年龄的函数命名为cmp_by_age,将两个void*类型的形参强转成struct stu*类型,访问它们的age元素并相减

int cmp_by_age(const void* e1, const void* e2)
{
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}

首先我们按照姓名来排序,比较年龄的函数命名为cmp_by_name,将两个void*类型的形参强转成struct stu*类型,访问它们的name元素,可以用strcmp比较字符串间的大小

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}

完整代码:

#include <stdio.h>
#include <string.h>

struct stu
{
	int age;
	char name[10];
};

int cmp_by_age(const void* e1, const void* e2)
{
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}

int main()
{
	struct stu arr[3] = { {18,"jack"},{30,"andy"},{25,"ride"} };
	int size = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, size, sizeof(arr[0]), cmp_by_age); //按照年龄排序
	qsort(arr, size, sizeof(arr[0]), cmp_by_name); //按照姓名排序
	return 0;
}

4.模仿qsort的功能实现一个通用的冒泡排序

模仿qsort函数实现冒泡排序,改进后的冒泡排序的函数列表中应与qsort函数一样

void BubbleSort(void* base, size_t size,size_t width,int(*cmp)(const void* e1,const void*e2))

在以往的冒泡排序中,有两层循环,在两层循环中有一个比较两个数大小的if语句,在if语句中有一个交换语句
在改进型的冒泡中,也都是这些语句,只不过改进的是if语句中判断两个数大小和交换函数而已

在改进的冒泡排序中,比较两个元素的大小是调用额外定义出的cmp函数
但是怎么将两个待比较的元素传到cmp函数中是个问题,因为接收进来的数组是void*类型的,不知道元素具体是什么类型,无法用下标去访问所以只能将base强转成char*类型,通过指针的偏移量去访问每个元素,每两个元素中间隔着一个width字节的宽度,所以用(char*)base+j*width取出第一个元素的地址,用(char*)base+(j+1)*width取出第二个元素的地址

放到cmp函数中进行比较

cmp((char*)base + j * width, (char*)base + (j + 1) * width)

紧接着如果两个元素需要进行交换,就要使用交换函数,还是因为不知到元素是什么类型的,所以还是一个字节一个字节得交换

void swap(char* buf1, char* buf2, int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;

	}
}

此时,模仿qsort函数实现冒泡排序就完成了,下面是完整代码:

#include<stdio.h>
#include <string.h>

struct stu
{
	int age;
	char name[10];
};

int cmp_by_age(const void* e1, const void* e2)
{
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

void swap(char* buf1, char* buf2, int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;

	}
}

void BubbleSort(void* base, size_t size,size_t width,int(*cmp)(const void* e1,const void*e2))
{
	int i = 0;
	for (i = 0; i < size-1; i++)
	{
		int j = 0;
		for (j = 0; j < size - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

void test1()
{
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, size, sizeof(arr[0]), cmp_int);
}

void test2()
{
	struct stu arr[3] = { {18,"jack"},{40,"andy"},{35,"mary"} };
	int size = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, size, sizeof(arr[0]), cmp_by_age);//按照年龄排序
	BubbleSort(arr, size, sizeof(arr[0]), cmp_by_name);//按照姓名排序
}

int main()
{
	test1();

	test2();
}

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

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

相关文章

iOS 内存泄漏检测 Instruments Leaks

Xcode 中 按住 command I 或者菜单栏 Product – Profile 2. 双击 Leaks 或者按 choose&#xff0c;打开 Leaks 面板 3. 在显示的 Leaks 面板中&#xff0c;点击左上角红色点&#xff0c;即可运行内存检测。 4. 在运行过程中如果发现Leak Checks&#xff08;如图&#xff09;…

【C进阶】C进阶练习编程题

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C语言进阶 ⭐代码仓库&#xff1a;C Advanced 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们…

关闭“此版本的Windows不再支持Microsoft Edge”提示

在Win7中&#xff0c;安装Microsoft Edge&#xff0c;升级到“109.0.1518.55 (正式版本)”后&#xff0c;打开Edge会弹出提示&#xff1a; 此版本的 Windows 不再支持 Microsoft Edge。升级到 Windows 10 或更高版本&#xff0c;以从 Microsoft Edge 获取常规功能和安全更新。 …

ESPNet: 自动驾驶领域轻量级分割模型

论文标题&#xff1a;ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation 论文地址&#xff1a;https://arxiv.org/pdf/1803.06815v2.pdf 开源地址&#xff1a; https://github.com/sacmehta/ESPNet 论文思想 ESPNet是用于语义分割的轻量…

一文弄懂Linux虚拟机网络配置

文章目录计算机网路基础网络连接模式Bridged&#xff08;桥接模式&#xff09;NAT&#xff08;地址转换模式&#xff09;Host-Only&#xff08;仅主机模式&#xff09;Linux常用网络相关命令ifconfig&#xff1a;配置网络接口ping&#xff1a;测试主机之间网络连通性修改ip地址…

Threejs 导入动态模型 - 兔子岛

Threejs 动画模型GLTF加载器&#xff08;GLTFLoader&#xff09; glTF&#xff08;gl传输格式&#xff09;是一种开放格式的规范 &#xff08;open format specification&#xff09;&#xff0c; 用于更高效地传输、加载3D内容。该类文件以JSON&#xff08;.gltf&#xff09;…

产品心理学:福格行为模型详解与应用

​Fogg说人的行为由动机&#xff0c;能力和触发条件这三要素组成&#xff0c;这三个同时都满足时行为才会发生。用一个等式来简化就是&#xff1a; BMAT 其中B是Behavior行为&#xff0c;M是Motivation 动机&#xff0c;A是Ability能力&#xff0c;T是Triggers触发。 行为的发…

“华为杯”研究生数学建模竞赛2004年-【华为杯】B题:实用下料的数学模型(附优秀论文)

赛题描述 “下料问题(cutting stock problem)”是把相同形状的一些原材料分割加工成若干个不同规格大小的零件的问题,此类问题在工程技术和工业生产中有着重要和广泛的应用. 这里的“实用下料问题”则是在某企业的实际条件限制下的单一材料的下料问题。 一个好的下料方案首先…

JavaEE4-Spring使用

目录 1.存储Bean对象到Spring容器中 1.1.创建Bean 1.2.将Bean注册到Spring容器中 1.2.1.第一次存储Bean&#xff08;可选&#xff0c;如果是第二次及以后&#xff0c;此步骤忽略&#xff09; 1.2.2.添加Bean标签 2.从Spring容器中获取并使用Bean对象 2.1.创建Spring上下…

ADC架构_Flash

ADC架构_Flash 比较器做1位ADC Vin > Vref Vout High&#xff08;1&#xff09; Vin < Vref Vout Low&#xff08;0&#xff09; 比较器是组建集成ADC的内部基本而又关键的模块&#xff1b;在比较器应用在ADC中一般要求很高的分辨率&#xff0c;也就是很小的差分输…

Spring Security 认证研究

2 Spring Security 认证研究 2.1 Spring Security介绍 认证功能几乎是每个项目都要具备的功能&#xff0c;并且它与业务无关&#xff0c;市面上有很多认证框架&#xff0c;如&#xff1a;Apache Shiro、CAS、Spring Security等。由于本项目基于Spring Cloud技术构建&#xff…

java数组2023014

数组&#xff1a;首先数组也是一种类型 Java的数组要求所有的数组元素具有相同的数据类型。因此&#xff0c;在 一个数组中&#xff0c;数组元素的类型是唯一的&#xff0c;即一个数组里只能存储一种数据类型的数据&#xff0c;不能存储多种数据类型的数据。 注意&#xff1a; …

MySQL数据库约束(主键约束,外键约束详解)

关系型数据库的一个重要功能:需要保证数据的"完整性",可以通过人工的方式来观察确认数据的正确性,这种方式是可行的,但是不合适,因为人为控制的方式势必会存在疏忽,导致一些错误没有被检查出来,所以MySQL数据库定义了一些约束来帮助程序员更好的检查数据的正确系!一,…

git cherry-pick 教程

对于多分支的代码库&#xff0c;将代码从一个分支转移到另一个分支是常见需求。 这时分两种情况。一种情况是&#xff0c;你需要另一个分支的所有代码变动&#xff0c;那么就采用合并&#xff08;git merge&#xff09;。另一种情况是&#xff0c;你只需要部分代码变动&#x…

目标检测-yolov1的详细理解(代码和原理)

目标检测算法&#xff0c;主要分为两类&#xff1a; 一类是以R-CNN为代表的两阶段检测算法&#xff0c;将目标检测任务分为边界框回归和物体分类两个模块二是yolo系列算法&#xff0c;是将目标检测任务看作是回归任务。 原理 yolov1将图像划分为S*S的网格&#xff0c;如果检测…

计网必会:应用层结构体系

文章目录什么是应用层应用层原理P2P模式C/S模式进程的通信原理客户和服务器进程进程发生运输服务类型TCP服务UDP服务服务选择什么是应用层 应用层原理 P2P模式 两台主机相互通信互为服务器&#xff0c;互为主机&#xff0c;可以发现&#xff0c;这样的模式下&#xff0c;如果是…

【Java寒假打卡】JavaWeb-TomCat发布动态资源

【Java寒假打卡】JavaWeb-TomCat发布动态资源Servlet的介绍实现步骤Servlet的执行流程Servlet的介绍 Servlet是运行在Java服务器端的程序&#xff0c;用于接受和响应来自客户端基于HTTP协议的请求如果想实现Servlet的功能&#xff0c;可以通过实现javax.servlet.Servlet接口或…

三、QML开发之qml 语言基础

QML就是用来编辑和生成Quick界面的语言&#xff0c;所以在开发界面之前一定要了解基础的QML语言基础知识&#xff0c;接下来从介绍qml语法如何编写&#xff0c;变量和属性 对象的简要说明&#xff0c;通过本节的学习能够达到简单的加载图片 和设置lable标签存放位置&#xff0c…

【零基础】学python数据结构与算法笔记14-动态规划

文章目录前言88.动态规划介绍89.钢条切割问题90.钢条切割问题&#xff1a;自顶向下实现91.钢条切割问题&#xff1a;自底向上实现92.钢条切割问题&#xff1a;重构解93.最长公共子序列最长公共子序列&#xff1a;实现总结前言 学习python数据结构与算法&#xff0c;学习常用的…

CSS+JS 弹窗

弹窗 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>弹窗</title><style type"text/css">.alert {display: none;justify-content: center;align-items: center;width: 100%;height: 100vh;backgro…