目录
前言
提示:此文章可搭配B站鹏哥视频食用(传送门)
一、解释qsort函数
1.函数原型
void qsort(
void *base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *)
);
头文件:<stdlib.h>
功能:对传输来的数据进行排序(默认排序顺序由用户决定)
base:传输来数据首元素地址
nmemb:数据的元素个数
size:每一个元素所占字节个数
(*compar)(const void *, const void *):
由用户自定义的比较函数指针,,传输数据类型是void类型,返回值类型是int,int (*)(const void *, const void *)是函数指针,comper是用户决定传输的比较函数。
2.深度解析comper比较函数
int (*compar)(const void * e1, const void * e2)
comper函数可由用户定义比较类型,输入类型e1和e2也有用户定义
返回值大于0,e1指向元素排序被安排在e2指向元素的前面
返回值小于0,e1指向元素排序被安排在e2指向元素的后面
返回值等于0,e1指向元素排序和e2排序位置不变
使用方法一(整形升序排列为例子)
int comper_int(const void* e1, const void* e2)
{
if (*(int*)e1 - *(int*)e2 > 0)
{
return 1;
}
else if (*(int*)e1 - *(int*)e2 < 0)
{
return -1;
}
else
{
return 0;
}
}
1.首先将e1和e2两个指针强制转化成int*的指针,然后解引用得到两个数据
2.之后将两个数字相减比较大小,
如果大于0,则表示e1比e2大,所以返回1
如果小于0,则表示e1比e2小,所以返回-1
如果等于0,则表示e1一样e2大,所以返回0
由于是升序排列,当e1比e2大时返回1则会让e1排序在e2后面,反之,如果是降序排列,当e1比e2大时返回-1则会让e1排序在e2前面,而当两者相等时,就不会发生排序。
使用方法二(整形升序排列,简化版本)
int comper_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
当e1大于e2时返回正数,当e1小于e2时返回负数,当e1等于e2时返回0
二、函数使用
1.int
刚才只是简单的介绍一下函数部分,接下来详细介绍使用方法。
1.首先我们创建一个整形数组,将数组的首地址和数组长度,和数组每一个元素的字符长度,以及我们比较函数的的地址传送出去。
void text2(Void)
{
int arr[] = { 1,2,5,6,7,9,8,4, };
int len = sizeof(arr) / sizeof(arr[0]);
qsort(arr, len, sizeof(arr[0]), cmp_int);
print_arr(arr, len);
}
2.比较函数中我们首先将两个需要比较交换顺序的元素比较大小,并且返回相对应的值。
int cmp_int(const void* e1, const void* e2)
{
if (*(int*)e1 - *(int*)e2 > 0)
{
return -1;
}
else if (*(int*)e1 - *(int*)e2 < 0)
{
return 1;
}
else
{
return 0;
}
}
3.之后我们用打印数组的方式打印数组
void print_arr(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
2.结构体
1.首先创建一个结构体变量
struct Stu
{
char name[20];//名字
int age;//年龄
float socre;//成绩
};
2.将结构体中的三个变量分别传送到qsort中分别比较
void test1(void)
{
struct Stu arr[] = { {"zhangsan", 20, 87.2f}, {"lisi", 30, 21.15f}, {"wangwu",40,2.54f} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_socre);//成绩
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//年龄
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//名字
printf_stu(arr, sz);
}
3.成绩比较函数:由于成绩定义为浮点数,所以不能用传统简介写法,直接传送两者差值,不然两者差值若为0.xxxx的话返回值不为0却当作0返回了。
由于是结构体类型,所以需要强制转化成结构体类型struct Stu*的指针然后再通过结构体类型解引用(->)得到数据
int cmp_stu_by_socre(const void* e1, const void* e2)
{
if (((struct Stu*)e1)->socre > ((struct Stu*)e2)->socre)
{
return 1;
}
else if (((struct Stu*)e1)->socre < ((struct Stu*)e2)->socre)
{
return -1;
}
else
{
return 0;
}
}
4.由于name是字符串,所以我们要用字符串的方式比较。
strcmp,一次比较每一个字符对应的ASCII大小,如果e1大于e2则返回1,小于则返回-1,相等的话就依次往后面继续比较。
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
5.由于age是整形,所以可以直接用传统简单的写法直接返回
int cmp_stu_by_age(const void* e1, const void* e2)
{
return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
6.打印结构体类型
void printf_stu(struct Stu arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%-10s %-3d %-5.2f\n", arr[i].name, arr[i].age, arr[i].socre);
}
printf("\n");
}
三、手动实现函数
1.我们用一个整形数组为例子
void text3()
{
int base[] = { 9,8,7,6,5,4,3,2,1,0 };
int len = sizeof(base) / sizeof(base[0]);
my_qsort(base, len, sizeof(base[0]), cmp_int);
print_arr(base, len);
}
2.核心代码(两段)
1.首先依次接受传输进来的首元素的地址,数据长度,数据每一个元素的所占的字节长度,以及决定比较的函数
2.在比较两个元素时,我们需要将首元素的地址初始化为字符大小,然后将从首元素开始往后走一个元素的宽度,这样我们就确定了一个元素的起始位,和从这个起始位开始往后走所占的字节长度,于是我们确定了相邻的两者元素,我们把相邻的两个元素进行我们所需类型的比较
3.当我们交换两个函数时,不仅要传送两个相邻的元素,还要将元素所占的字节宽度也传送过去,这样我们就能从两个元素的第一个起始字符开始向后一个一个进行字节替换。
void my_qsort(const void* base, int len, int width, int(*my_cmp)(const void* e1, const void* e2))
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
//if(base[j] > base[j + 1])
if (my_cmp((char*)base + j * width, (char*)base + (j + 1)*width) > 0)
{
my_swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
4.传送过来的两个元素,我们一个一个将对应的字节交换顺序。
void my_swap( char* buf1, char* buf2, int width)
{
for (int i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
5.打印排序后的数组
void print_arr(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
6.比较函数
int my_cmp(const void* e1, const void* e2)
{
if (*(int*)e1 - *(int*)e2 > 0)
{
return -1;
}
else if (*(int*)e1 - *(int*)e2 < 0)
{
return 1;
}
else
{
return 0;
}
}
四、升级使用方法
比较函数:如果要求偶数排前面,奇数排后面,可以这样写
int Qua(int x) {
return x % 2;
}
int cmp(const void *p1, const void *p2) {
return Qua(*(int *)p1) - Qua(*(int *)p2);
}