甜甜C语言—qsort()函数(快速排序)

  • Post author:
  • Post category:其他




一:前言

最近做结构体专题了解了qsort()函数称为快排函数,显然就是实现快速排序,

排序原则是通过你所传递的具体参数——Compare()函数决定的。



二:头文件

#include<stdlib.h>



三:函数原型

void qsort (void* base, size_t num, size_t size,int (

compar)(const void

,const void*)); //详细版本

void qsort(void *base,int n,int size,Compare);//个人记忆版本,Compare代表compare() 函数



四:函数功能实现



(一)第一参数

🚀🚀

第一个参数 void *base :

代表需要排序的任意数据类型的一组数据的首地址,

作者现在遇到的大部分都是数组型,或者是结构体型。

base之所以定义成

空指针(void *)类型

,是为了告诉编程者传入的指针类型是

由编程者自行决定

的。

(刚开始我也想了很久,搞不懂为啥传入的是空指针,现在想想好像它的意思就是为了告诉你,你需要传入一个指针,不管是什么类型,我只需要你传入的是个指针就行。)



(二)第二参数

🚀🚀

第二个参数 int n

: 代表待排序的数组的长度。

这个应该是比较容易理解的,就像我们自己平常定义函数的时候一样,

我们习惯把数组的大小通过参数传递的方式来告诉函数,便于函数的编写。

在此就不再详细介绍🍀🍀



(三)第三参数

🚀🚀

第三个参数 int size :

代表被排序的那组数据中的

一个对象

所占的字节数。

因为函数在编写的时候并不知道用户会传递进来一个什么类型的指针,

自然也就不知道数据的空间大小,qsort()函数内部的指针移动就无法进行。

为了解决这个问题,我们把被排序的那组数据的一个对象所占用的空间大小以函数参数的形式传递给qsort()函数,

在qsort()函数内部把指针强制转化称为(char*)类型,

我们都知道(char*)指针每次移动的大小正是一个字节,

利用这个特性再加上传入的参数size,

qsort()函数内部就可以轻松的实现指针的移动了

这就是传入第三个参数的目的。

🔥🔥

当然,我们了解了解就行了,关键还是要会使用qsort()函数实现快速排序。



(四)第四参数

🚀🚀 第四个参数 Compare :

实际上传入的是一个函数,这里代表

指向函数的指针

Compare()函数是为了告诉qsort()函数需要执行的排序方式,是顺序或者是逆序。

Compare()函数是需要编程者根据实际的要求进行编写的。

qsort()函数关键之处就在于Compare()函数的编写。

下面将着重讲解Compare()函数的编写。



五:Compare()函数的编写

在qsort()函数的函数原型中我们可以看到,qsort()函数给出了关于Compare()函数的特定格式

int (

compar)(const void

,const void*);

需要注意的是

1️⃣Compare()函数的返回值是一个int 类型

第1个参数 > 第2个参数 返回大于0的整数

第1个参数 = 第2个参数 返回整数0

第1个参数 < 第2个参数 返回小于0的整数

2️⃣void*指针不能解引用,需要

强制类型转换为对应的指针类型



(一)整型数据排序

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


注意:

1️⃣void*指针不能解引用,需要强制类型转换为对应的指针类型

2️⃣“对应的指针类型”这需要由你传入的参数一来决定,如果传入的是浮点型数组,那么就需要强制转换成(float *)指针,等等

3️⃣返回值必须是int类型,因为本例中指针p1和p2指向的数据本来就是int类型,所以本例中的强制转换可以去掉。



(二)浮点型数据排序

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


注意:

1️⃣强制转化了指针的类型,对应了整形数据排序中的第二条注意点

2️⃣强制转换返回值为int 类型不能省略,因为指针p1和指针p2指向的数据类型是float类型,运算后需要强制转换

3️⃣强制转换具有陷阱,建议写法 : return (*(float

)p1-

(float *)p2)>0? 1:-1;

简单举个例子,读者就会明白为什么建议这样编写:

在这里插入图片描述

简单来说就是 (int)0.1=0;

假如说我们进行比较的两个数相差的大小恰巧是-1到1之间的小数,

那么强制转化后全部变成0,

而Compare()函数的排序机制是

第1个参数 > 第2个参数 返回大于0的整数

第1个参数 = 第2个参数 返回整数0

第1个参数 < 第2个参数 返回小于0的整数

显然结果会出错。



(三)字符串排序



1.字符串大小

需要借助strcmp()函数

int Compare_char1(const void *p1,const void *p2)
{
	return strcmp((char *)p1,(char *)p2);
 } 


注意:

1️⃣强制转化指针类型为(char*)类型

2️⃣在此不需要强制转化返回值为int类型,因为strcmp()函数返回的值便是int类型,符合要求。

当然如果不放心的话,强制转化一下也不多余,多加了一层保险。



2.字符串长度

int Compare_char2(const void *p1,const void *p2)
{
	return (int)(strlen((char *)p1)-strlen((char *)p2));
 } 
 


注意:

1️⃣此处的返回值强制转换int类型,同样可以省略,因为字节数本来就是int类型,经过减法运算后依旧是int类型

2️⃣(其他注意点类比上述例子,没有必要的注意点,不再详细叙述)



(四)结构体排序

通过结构题中的数据成员的大小进行结构体的排序

#include<stdio.h>
#include<stdlib.h>
typedef struct Student
{
	char name[8];
	char student_id[12];
	float weight;
}st;

..........

int Compare_struct(const void *p1,const void *p2)
{
	return (((st *)p1)->weight-((st *)p2)->weight) >0? 1:-1;
 } 


注意:

return (((st *)p1)->weight-((st *)p2)->weight) >0? 1:-1;

1️⃣(st *)p1:强制转换成对应的结构体类型(st *);

2️⃣((st *)p1)->weight:访问指针p1指向的结构体中的成员weight;

3️⃣(((st *)p1)->weight-((st *)p2)->weight):比较两个结构体成员weight的大小;

4️⃣(((st *)p1)->weight-((st *)p2)->weight) >0? 1:-1; 使用 ?:运算符;(类比浮点型数据的排序)



(五)实现相反顺序的排序

只需在定义Compare()函数时

把指针p1和指针p2调换一下位置

即可。

例如:

//实现从小到大的排序 
int Compare_int_1(const void *p1,const void *p2)
{
	return (int)(*(int *)p1-*(int *)p2);
 } 
 
 //实现从大到小的排序 
int Compare_int_2(const void *p1,const void *p2)
{
	return (int)(*(int *)p2-*(int *)p1);
 } 
 



六:如何使用qsort()函数

在定义好Compare()函数之后,我们在调用qsort()函数的时候

直接传入定义的函数名

即可。


前面介绍第四个参数Compare()的时候,说明了参数为指向函数的指针,传入函数名就相当于传入了我们定义的函数的“地址”,对应指向函数的指针(指向指针的函数在此不进行详细的介绍,感兴趣的读者欢迎浏览我的另一篇关于指针问题的博客,链接奉上)


甜甜C语言——指针(全面总结)

例如:

int array_1[100];

 float array_2[100];
 
 st array_3[100];
  ......
 qsort(array_1,100,sizeof(int),compare_int);
 
 qsort(array_2,100,sizeof(float),compare_float);
 
 qsort(array_3,100,sizeof(st),compare_struct);



七:财大OJ例题


新年快到了,计算机学院新年晚会正在筹备中,今年有一个新创意:来参加晚会的所有学生都有礼物(一根棒棒糖)。老师把买棒棒糖的任务交给小明了,并指定了要买的棒棒糖的品牌和品种。俗话说得好,货比三家不吃亏。小明来到了商店,看了各个店铺里这种棒棒糖的价格,不仅如此,他还记住了每个店铺的存货量。已知小明打算购买n根棒棒糖,问他最少要花多少钱?

输入

第一行输入一个整数n,表示要购买的棒棒糖数量;第二行是一个整数m(1<=m<=10),表示明明考察过的店铺的数量;接下来m行,每行两个数,表示该店铺中棒棒糖的价格和数量,价格为一实数(最多两位小数),数量为一整数。

输出

输出一个实数,保留两位小数,表示最小费用。

样例输入

100

4

0.5 50

0.33 30

1 80

0.6 40

样例输出

46.90

答案样例:

🙉🙉主要学习部分是qsort()函数的调用,以及Compare()函数的定义

#include<stdio.h>
#include<stdlib.h>
//定义结构体
typedef struct candy
{
	double price;
	int goods_num;
 }ca;
 //定义函数,实现为结构体赋值
 ca candy_gets(ca *cf)
 {
 	scanf("%lf %d",&cf->price,&cf->goods_num);
 	return *cf;
  } 
//按照单价进行排序,从底到高
//利用快排函数qsort();或者普通排序,都可 
//定义compare_price()函数
//注意:函数要求返回整型,强制转换整型会出错,(int)0.01=0  不能简单返回强制转换 
int compare_price(const void *cf_1,const void *cf_2)
{
	return (((ca*)cf_1)->price-((ca*)cf_2)->price)>0? 1:-1;    //perfect 
 } 
 //构造函数,开始买买买 
 double  purchase(ca *cf,int n,int sum)
 {
 	double price_s=0;
 	int i=0,j=0;
 	int sum_s=sum;  //记下原始sum的值 
 	while(sum>0)
 	{
 		sum-=cf[j].goods_num ;
 		j++;
	 }
	//sum的值已经改变,一定要记下原始的sum值 
	 for(i=0;i<j-1;i++)
	 {
	 	price_s+=cf[i].goods_num *cf[i].price;
	 	sum_s-=cf[i].goods_num;
	 }
	 price_s+=sum_s*cf[j-1].price;
	 return price_s;
 }
 int main()
 {
 	int sum,n,i;
 	double price_s;
 	scanf("%d %d",&sum,&n);
 	ca *candy_s=(ca *)malloc(n*sizeof(ca));
 	for(i=0;i<n;i++)
 	{
 		candy_s[i]=candy_gets(&candy_s[i]);
	 }
	 qsort(candy_s,n,sizeof(ca),compare_price);
	 price_s=purchase(candy_s,n,sum);
 	printf("%.2lf",price_s);
 	return 0;
 }



八:结语

🚀🚀🚀以上就是我对qsort()函数相关知识点的大概总结,希望对读者能有帮助!!💕💕

🚀🚀🚀内容不全面,可能存在错误的地方,如发现有错欢迎私信进行纠正,不尽感激。💕💕

🚀🚀🚀一名编程初学者,第七篇博客,希望能够得到大家的支持,一起努力吧,加油🌻🌻🌻🌻🌻!!

拜拜🐾🐾🐾🐾🐾🐾

💕💕

💕💕

💕💕

💕💕



版权声明:本文为m0_64246256原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。