前言
“我告诉我自己要涂自己喜欢的口红,我告诉我自己要好好生活。”
“我是说,我期待一个春天”
一、qsort函数的介绍
qsort函数是一个用来排序的库函数,底层是用快速排序的思想实现的。
二、引入——由冒泡排序函数引入qsort排序
冒泡排序的思想为:两两相邻元素进行比较,满足顺序就不交换,不满足顺序就交换。
n 个数字要进行n-1趟排序
代码实现
//冒泡排序(升序版)
#include <stdio.h>
void Print(int* p, int n);
void Bubble_sort(int* p, int n)//排序函数
{
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (p[j] > p[j + 1])
{
int tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
Print(p, n);
}
void Print(int* p, int n)//打印函数
{
for (int i = 0; i < n; i++)
{
printf("%d ", p[i]);
}
printf("\n");
}
int main(void)
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_sort(arr, sz);
return 0;
}
代码运行结果展示
写出来以后,我们仔细观察,就会发现这个函数的性能很差。如图,因为函数参数已经固定,这个函数只能排序整型数组,而qsort函数可以排序任意类型的数据,甚至是字符串以及结构体。那qsort是如何做到的呢?
三.qsort官网介绍
这是来及cplusplus官网上对于qsort的介绍,链接放在这里,读者可自行阅读 添加链接描述
qsort函数原型如下
头文件
函数原型
函数参数
中文注解
//以下为qsort函数原型,以及四个参数的介绍
void qsort (void* base, //void *指针,指向待排序的数组的第一个元素(可理解为基础地址)
size_t num, //base指向的待排序数组中的元素的个数
size_t size,//base指向的待排序数组中的元素的大小(以字节为单位)
int (*compar)(const void*,const void*));//函数指针,指向的为两个函数的比较函数
注意第四个参数,是储存比较函数地址的函数指针。厂家是qsort函数的实现者,我们是qsort函数的使用者,我们明确的知道我们要排序的是什么数据,以及这些数据如何比较,所以,两个函数的比较函数应该由我们提供。
这里我们把第四个参数单独拿出来研究。
int (*compar)(const void * ,const void *));,这是一个函数指针
那么传给这个指针地址的函数原型就是
int Compare(const void *a, const void *b)
该函数有两个参数,a与b
a中放的是第一个要比较的元素的地址,b中放的是第二个要比较的元素的地址
这里我们把传给这个指针地址的函数原型拿出来
注意:void* 类型的指针,是无具体类型的指针,不可读不可写入(不可直接解引用),不可加减不可计算。需要强转才可以使用。
如if (* (int*)p1 > * (int*)p2)
四.qsort函数的使用举例
排序整数的代码实现
//使用qsort函数排序
#include <stdio.h>
#include <stdlib.h>
void Print(int* p, int n)//打印函数
{
for (int i = 0; i < n; i++)
{
printf("%d ", p[i]);
}
printf("\n");
}
int Compare(const void* p1, const void* p2)//比较函数的实现
{
if (*(int*)p1 > *(int*)p2)//void *类型的指针,是无具体类型的指针,不可读不可写入(不可直接解引用),不可加减不可计算
{
return 1;
}
else if (*(int*)p1 == *(int*)p2)//也可作差,返回差值
{
return 0;
}
else
{
return -1;
}
}
void test1()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), Compare);
Print(arr, sz);
}
int main(void)
{
test1();
return 0;
}
运行结果展示
结构体排序的代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu
{
char name[20];
int age;
};
int Comparestructname(const void* p1, const void* p2)//按名字排序
{
return strcmp(((struct stu*)p1)->name , ((struct stu*)p2)->name);
}
int Comparestructage(const void* p1, const void* p2)//按照年龄排序
{
return (((struct stu*)p1)->age - ((struct stu*)p2)->age);
}
void test2(void)
{
struct stu arr[3] = { {"xiaotzaozi, 19"}, {"xiaotao", 20}, {"sanxin", 21} };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), Comparestructname);
Print(arr, sz);
}
int main(void)
{
test2();
return 0;
}
由于小桃子结构体忘记的差不多了,不太清楚怎么打印结构体数组中的字符串数组,就此做罢了。
从调试可以看出各个变量的变化。
五.改造冒泡排序,让其变成qsort
在冒泡排序的基础上,不改变算法(即仍然使用冒泡排序的思想),实现qsort,而这个函数可以排序任意类型的数据。
我们可以发现,冒泡排序只可以比较整数,那如果要比较字符串以及结构体呢?——那就要更改交换数字的部分。排序对象不一致,排序比较的方法就要更改。
于是乎,我们就想到把两个元素比较的方法封装成一个函数,然后把函数地址传给排序函数。
重点:排序方法的实现
代码展示
//使用伪qsort函数——Bubble_sort来排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Bubble_sort(void* base, size_t sz, size_t width, int (*Compare)(const void* p1, const void* p2));
int Compare(const void* p1, const void* p2);
void Print(int* p, int n);
void Print(int* p, int n)//打印函数
{
for (int i = 0; i < n; i++)
{
printf("%d ", p[i]);
}
printf("\n");
}
int Compare(const void* p1, const void* p2)//比较函数的实现
{
return *(int*)p1 - *(int*)p2;
//等价于
//if (*(int*)p1 > *(int*)p2)//void *类型的指针,是无具体类型的指针,不可读不可写入(不可直接解引用),不可加减不可计算
//{
// return 1;
//}
//else if (*(int*)p1 == *(int*)p2)
//{
// return 0;
//}
//else
//{
// return -1;
//}
}
void test1()//使用伪qsort函数
{
int arr[] = { 1,5,3,4,7,6,2,8,9,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_sort(arr, sz, sizeof(arr[0]), Compare);
Print(arr, sz);
}
//交换函数,一个一个字节交换
//一大块空间要交换,切成一小块一小块空间去交换
//妙啊
void swap(char* p1, char* p2, size_t n)
{
for (int i = 0; i < n; i++)
{
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2++;
}
}
void Bubble_sort(void * base, size_t sz, size_t width, int (* Compare)(const void * p1, const void * p2))//伪qsort函数实现
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
//比较
/*if (p[j] > p[j + 1])
{
int tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}*/
//比较。比较函数要自己写
if (Compare((char *)base + j * width, (char *)base + (j + 1)* width) > 0)
{
//交换
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main(void)
{
test1();
return 0;
}
总结
就如我这张图片上写的一样,好累,一个字都敲不出来,感觉下一秒就要闭上眼睛了,但是睡不着,因为很开心,这是我第一次写这么复杂的代码。今天把大家诟病的一款浏览器删掉了,换了一个浏览器,好像电脑是快了点,但联网又有了点问题,慢慢来,不懂的关于电脑的问题,慢慢解决。
和家人分享就是很开心嘞。
今天和大家的分享也就到这里了,如果你觉得有帮助的话,可不可以给小桃子点个赞呢?
感谢你的阅读。
我是小桃子,我爱这个世界。
2024年3月3日21:34 礼拜天 晴