当字节在指尖跳动时,你是否能谱写这青春的赞歌
大家好,初次见面,请多关照🙈🙉🙊。在本篇博客中,新一将会为大家介绍qsort函数使用及其模拟。(以下结果均在VS2022中编译)希望在方便自己复习的同时也能帮助到大家。😜😜😜🥇🥈🥉
废话不多说,直接进入我们的文章。
文章目录
一.🥇 温故而知新:冒泡排序
1.1🥈 升序——冒泡
我们先来实现一下我们熟悉的冒泡排序函数😎😎😎
冒泡排序: 冒泡排序是众多排序算法中的一种,其余排序算法还有快速排序,希尔排序,选择排序,插入排序 等等,在后续的学习中新一会带大家把基本的排序算法都遍历一遍,敬请期待🙈🙉🙊,我们先用一张动图来了解我们的冒泡排序算法
这是其中的一趟排序,总结下来就是让大数沉底
1.2🥈 冒泡代码实现
根据上述分析,我们知道要想实现整个数组的排序,就必须多趟排序,多趟比较 🧐🧐🧐
void bubble_sort(int arr[], int sz)//传入arr数组,以及元素个数
{
int i = 0;
for (i = 0; i < sz - 1; i++)//每个元素的排序
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)//大元素沉底
{
if (arr[j] > arr[j + 1])//元素交换
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
值得注意的是,每趟排序沉底的元素后续不需要比较,即每趟冒泡排序次数依次递减,此时我们再引入main函数看看实现效果。
void print(int arr[], int sz)//打印函数
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void bubble_sort(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void test_bubble()//将其封装在一个函数内,方便后续修改main函数
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//冒泡排序
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
print(arr, sz);
}
int main()
{
test_bubble();
return 0;
}
但是我们试想,我们自己实现的这个冒泡排序函数,能不能排序浮点型,结构体,字符型数据?,显然是不行的,这样的话我们使用起来就会造成一定的麻烦。
二.🥇 步入正题:qsort函数
2.1🥈 qsort函数介绍
qsort函数: qsort函数是基于快速排序算法实现的一个排序的函数,它的牛逼之处在于能一键排序多种类型的数据,不再拘泥于排序一种类型的数据,我们先来看看它的参数类型
这是个查找函数的网站,用起来十分方便快捷,推荐大家使用,网站我会放在评论区哦
🚀void qsort (void* base, size_t num, size_t size,int (* compar)(const void*,const void*));
头文件:#include<stdib.h>
第一个参数:void* base指目标数组的首元素地址
第二个参数:size_t num指目标数组的元素个数
第三个参数:size_t size指目标数组的元素的大小(占几个字节)
第四个参数:int (* compar)(const void*,const void*)这是需要操作者自己定义的一个函数,该函数第一个和第二个参数类型都是void*类型,由此实现多种数据类型的排序
🚀第四个参数返回类型是int类型,我们通过对上述图片的了解(最下面)知道了对于数组中任意两个元素:a b
a < b:返回值-1
a = b:返回值0
a > b:返回值1
注意a > b返回值只要为正即可,a < b时返回值为负即可
2.2🥈 qsort函数实现
2.21🥉排序整形
我们先来搭建好qsort函数的框架。
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void test_qsort_int()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//冒泡排序
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);//依据上述介绍的参数类型
print(arr, sz);
}
int main()
{
test_qsort_int();
return 0;
}
现在最重要的就是实现自己定义的cmp_int函数,其参数void*类型,表示空指针类型,我们可以通过强转来改变其数据类型,所以这个函数直接决定了我们所排序的数据类型 下面是新一写的cmp_int函数🤔🤔🤔
int cmp_int(const void* e1, const void* e2)
{
//void* 类型不能直接解引用,因为不确定访问几个字节
if (*(int*)e1 > *(int*)e2)//由于排序整形数据。强转为int*类型
{
return 1;
}
else if (*(int*)e1 == *(int*)e2)
{
return 0;
}
else
{
return -1;
}
}
这样会不会太麻烦呢?用if语句判断代码量太多,既然我们可以根据返回值的正负来判断大小,我们不妨直接相减
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;//巧解
}
这样就很巧妙的实现了cmp_int函数,结果读者可以自行验证哦。🙈🙉🙊
2.22🥉排序结构体
有了上一种类型的思想,相信实现结构体的排序对大家来说也不是难事。
struct Stu
{
char name[20];
int age;
double score;
};
int cmp_stu_by_age(const void* e1, const void* e2)//根据年龄对结构体排序
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//强转为所定义的结构体指针类型
}
int cmp_stu_by_name(const void* e1, const void* e2)//根据名字对结构体排序
{
return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}
void test_qsort_Stu()
{
struct Stu arr[3] = { {"zhangsan", 30, 55.5},{"lisi", 20, 88},{"wangwu", 50, 90.0} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//验证年龄
}
int main()
{
test_qsort_Stu();
return 0;
}
这里我们以调试的方式验证年龄。
这是之前未排序数据
这是排序后数据
排序成功
三.🥇 主体升华:bubble_sort函数改造
3.1🥈 整体框架
这里我们用改造的bubble函数来排序结构体
struct Stu
{
char name[20];
int age;
double score;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}
void test_bubblesortpro_Stu()
{
struct Stu arr[3] = { {"zhangsan", 30, 55.5},{"lisi", 20, 88},{"wangwu", 50, 90.0} };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort_pro(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
//主函数
int main()
{
test_bubblesortpro_Stu();
return 0;
}
3.2🥈 关键函数实现
其余函数实现都跟前面一样。那么现在主要工作就是 实现bubble_sort_pro(arr, sz, sizeof(arr[0]), cmp_stu_by_name)
void bubble_sort_pro(void* base, int num, int width, int (*cmp)(const void* e1, const void* e2))
{
int i = 0;
for (i = 0; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//if (arr[j] > arr[j + 1])//比较
if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
{
//交换
Swap((char*)base + width * j, (char*)base + width * (j + 1), width);
}
}
}
}
我们依照冒泡排序*的框架来构建我们改造后的函数,这里我们主要改造比较部分和交换部分
3.21🥉比较部分
由于我们需要考虑各种类型的数据,所以得让我们的比较具有普遍性,我们知道最小的数据类型的char型,那么我们不妨用char来当做我们的底层,在一个字节的基础上加上每个数据的大小width * j,即代表了每个元素之间的互相比较
🚀举个例子
对于数组int arr[5] = { 1,2,3,4,5 }, 假设首元素地址为1,占四个字节,第二个元素地址为5,占四个字节,当我们的 j 为0时,即表示比较地址1 +sizeof(int) * j地址 1 + sizeof(int) * (j + 1)类型,即第一个和第二个元素的比较
3.22🥉交换部分
我们要想实现各种数据类型的交换,必须还是以char为框架,以参数的形式传入width即数据元素大小实现交换
void Swap(char* pa, char* pb, int width)
{
int i = 0;
for (i = 0; i < width; i++)//逐字节交换
{
int temp = *pa;
*pa = *pb;
*pb = temp;
pa++;
pb++;
}
}
3.3🥈 结果验证
我们继续以调试的方式来验证
交换前
交换后
我们的结果得到了验证.
想对大家说的话
家人们,学到这里我们已经彻底学会了使用qsort函数以及其模拟实现🥳🥳🥳,后续新一会持续更新C语言进阶的有关内容,学习永无止境,技术宅,拯救世界!