导言:
在使用C语言编写代码时,我们总会遇到一些需要我们排序的内容。如果能有一个现成帮我们写好的库函数来进行排序就能让我们的效率大大提高,事实上C语言确实也有这样的函数,只需我们调用,而本文主要就qsort函数进行讲解。
目录
正文:
一.qsort函数的使用
qsort()函数是C语言标准库中提供的一个排序函数,用于对数组进行排序。它的函数原型如下:
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
参数说明:
- base:指向待排序数组的指针。
- num:待排序数组的元素个数。
- size:待排序数组中每个元素的大小(以字节为单位)。
- compar:比较函数,用于确定元素之间的顺序关系。
值得注意的是函数参数base是void*类型,这也意味着无论是基本数据类型还是自定义的数据类型都可以使用该函数进行排序,size_t是一种无符号整数类型,用于表示内存大小或对象大小。而最后一个参数是一个函数指针,传参时需要将函数名当作地址传入。下面进行详细的介绍:
compar比较函数的原型如下:
int compar(const void *a, const void *b);
参数说明:
- a和b:指向待比较的两个元素的指针。
比较函数应返回一个整数值,表示a和b的大小关系:
- 若返回值小于0,表示a小于b;
- 若返回值等于0,表示a等于b;
- 若返回值大于0,表示a大于b。
该函数由程序员自己完成,注意用来规定比较大小的类型以及规则,可以让排序变得更加灵活。
- 对于升序排序,比较函数应返回
a
和b
的大小关系。如果a
小于b
,则返回一个负数;如果a
等于b
,则返回0;如果a
大于b
,则返回一个正数。例如:int compare(const void *a, const void *b) { int num1 = *(int*)a; int num2 = *(int*)b; return num1 - num2; }
- 对于降序排序,比较函数应返回
b
和a
的大小关系。如果b
小于a
,则返回一个负数;如果b
等于a
,则返回0;如果b
大于a
,则返回一个正数。int compare(const void *a, const void *b) { int num1 = *(int*)a; int num2 = *(int*)b; return num2 - num1; }
- 对于自定义排序,比较函数可以根据具体需求进行编写。比较函数可以使用任何逻辑来比较
a
和b
,并返回相应的结果。
下面是qsort()函数进行排序的实例代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//自定义类型
struct Stu {
int age;
char name[20];
};
//基本数据类型
int cmp1(const void* p, const void* q) {
return (*(int*)p) - (*(int*)q);
}
//自定义数据类型
//1.按年龄排序
int cmp2(const void* p, const void* q) {
return ((struct Stu*)p)->age - ((struct Stu*)q)->age;
}
//2.按姓名排序
int cmp3(const void* p, const void* q) {
return strcmp(((struct Stu*)p)->name, ((struct Stu*)q)->name);
}
int main() {
//均采用升序
int arr1[10] = { 1,5,9,6,8,7,4,2,3,10 };//基本数据数组
struct Stu arr2[3] = { {20, "zhanhsan"},
{30,"lisi" },
{18,"wangwu"} };//自定义类型数组
int size1 = sizeof(arr1) / sizeof(arr1[0]);//arr1的长度
int size2 = sizeof(arr2) / sizeof(arr2[0]);//arr2的长度
qsort(arr1, size1, sizeof(arr1[0]), cmp1);//对arr1进行排序
qsort(arr2, size2, sizeof(arr2[0]), cmp2);//对arr2按照年龄大小排序
//qsort(arr2, size2, sizeof(arr2[0]), cmp3);//对arr2按照姓名排序
for (int i = 0; i < size1; i++) {
//对arr1整形数组的排序结果
printf("%d ", arr1[i]);
}
printf("\n");
for (int i = 0; i < size2; i++) {
//对arr2按照年龄排序的结果
printf("%d ", arr2[i].age);
}
printf("\n");
/*for (int i = 0; i < size2; i++) {
//对arr2按照姓名排序的结果
printf("%s ", arr2[i].name);
}*/
return 0;
}
运行结果如下:
由于调用时会有覆盖作用,所以这里只调用了一次arr2的年龄排序,如果调用姓名排序的话会按照字典序将姓名排好
总之,qsort()
函数是一个方便、高效的排序函数,可以用于对数组进行排序。通过编写合适的比较函数,可以灵活地指定排序的顺序,满足不同的排序需求。
二.qsort函数自实现
实际上qsort函数内部采用的排序算法是快速排序,但这里为了更加简单我们采用冒泡排序实现。
实现步骤:
1.我们先对应qsort函数写出函数的原型:
void my_qsort(void* base, int num, int sz, int (*cmp)(const void* e1, const void* e2)) {
2.我们采用冒泡排序的思想来进行内部的排序,先将冒泡排序的框架打好:
void my_qsort(void* base, int num, int sz, int (*cmp)(const void* e1, const void* e2)) { for (int i = 0; i <num-1; i++) { for (int j = 0; j <num-i-1 ; j++) { if () { } } } }
这里考虑到base的类型是void*,也就说我们不知道传进来的参数究竟是什么类型,自然我们也不能简单的用冒泡排序中的比较方式。我们先考虑if语句的条件,在冒泡排序中我们采用的是
if(base[j]>base[j+1]){ swap(base[j],basr[j+1]); }
而这里由于我们并不知道数据的类型,所以无法利用这种方法。但是我们可以用指针实现操作,无论是什么类型我们先强制类型转化成char*,然后再加上对应的 下标*sz 即可实现和base[j]一样的效果。而程序员使用此函数时会自己提供一个cmp函数,我们再在这里调用,实现对两者的比较。
同理swap函数的参数也是一样。
代码如下:
void swap(char* buf1,char* buf2,int sz) { for (int i = 0; i < sz; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 =tmp; buf1++; buf2++; } }
if (cmp((char*)base+j*sz,(char*)base+(j+1)*sz)>0){ swap((char*)base+j*sz,(char*)base+(j+1)*sz,sz); }
3.最后我们来实现swap函数,我们先思考冒泡排序中的处理。
void swap(int a, int b) { int tmp = a; a = b; b = a; }
由于我们已经知道swap函数的两个参数都是char*类型了,这就使得交换变得简单。我们只需要知道这中类型的字节大小,再将每个字节的内容进行交换,即可实现两未知元素的交换。代码如下:
void swap(char* buf1,char* buf2,int sz) { for (int i = 0; i < sz; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 =tmp; buf1++; buf2++; } }
最后我们来调用一下我们自己写的qsort函数试试,这里自定义数据类型我们选择调用按照姓名进行排序,代码如下:
#pragma warning(disable:4996)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu {
int age;
char name[20];
};
void swap(char* buf1,char* buf2,int sz) {
for (int i = 0; i < sz; i++) {
char tmp = *buf1;
*buf1 = *buf2;
*buf2 =tmp;
buf1++;
buf2++;
}
}
void my_qsort(void* base, int num, int sz, int (*cmp)(const void* e1, const void* e2)) {
for (int i = 0; i <num-1; i++) {
for (int j = 0; j <num-i-1 ; j++) {
if (cmp((char*)base+j*sz,(char*)base+(j+1)*sz)>0) {
swap((char*)base+j*sz,(char*)base+(j+1)*sz,sz);
}
}
}
}//基本数据类型
int my_cmp(const void* e1, const void* e2) {
return (*(int*)e1) - (*(int*)e2);
}
//自定义数据类型
//1.按年龄排序
int cmp2(const void* p, const void* q) {
return ((struct Stu*)p)->age - ((struct Stu*)q)->age;
}
//2.按姓名排序
int cmp3(const void* p, const void* q) {
return strcmp(((struct Stu*)p)->name, ((struct Stu*)q)->name);
}
void print(int n,int arr[]) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = { 4,1,5,3,2,8,7,9,6,10 };
my_qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]), my_cmp);
print(sizeof(arr) / sizeof(arr[0]), arr);
struct Stu arr2[3] = { {10, "张三"},
{20,"李四" },
{18,"王五"} };
int size2 = sizeof(arr2) / sizeof(arr2[0]);
//qsort(arr2, size2, sizeof(arr2[0]), cmp2);
qsort(arr2, size2, sizeof(arr2[0]), cmp3);
for (int i = 0; i < size2; i++) {
printf("%s ", arr2[i].name);
}
/*for (int i = 0; i < size2; i++) {
printf("%d ", arr2[i].age);
}*/
return 0;
}
运行结果如下:
可以看到排序成功了,虽然效率并不是很高,但是也能够将qsort函数的功能给还原出来了。
总结:
qsort函数在我们日常中使用的还是比较广泛的,在还没有学习很多排序算法时,qsort函数能够满足大部分的排序需求。同时,通过自实现qsort函数,我们不仅更加熟练了冒泡排序的思想和实现过程,还对qsort函数有了更深层次的理解。通过对qsort函数的自实现,我们可以更好地应对排序问题,并在实际开发中灵活运用。希望本篇文章能够对你有所帮助。