在介绍qsort这个神奇的函数之前,我想先介绍一个东西,叫冒泡排序,由此来引入一下
一、冒泡排序
而这个冒泡排序的核心思想就是:两两相邻的元素进行比较。(只能进行整型数据的排序)
下面给出代码:
void Change(int* arr,int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
void print(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz ; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
//进行升序
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Change(arr,sz);
print(arr,sz);
printf("\ncount=%d\n", count);
return 0;
}
但我们发现这样做循环次数过多,所以我们可以进行改进,无序时我们在交换,原本就有序时不去交换
int count = 0;
void Change(int* arr,int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//先假设它有序为真
int j = 0;
for (j = 0; j < sz - i - 1; j++)
{
count++;
if (arr[j] > arr[j + 1])
{
flag = 0;//一旦无序,在进行交换,
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
if (flag == 1)
{
break;
}
}
}
void print(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz ; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
//进行升序
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Change(arr,sz);
print(arr,sz);
printf("\ncount=%d\n", count);
return 0;
}
如果大家还有什么更加好的方法,欢迎大家在评论区留言。接下来就让我们了解qsort吧
二、qsort函数
1.功能
qsort是C语言的一个库函数,用来对数据进行排序,(对任意数据都能进行排序)。
这里第二个参数中提到的元素数就是你要排序的元素个数。
这里的返回值意思就是假设有两个元素a与b,当a>b,返回值为一个大于0的数字;当a<b,返回值为一个小于0的数字;当a=b,返回值为一个等于0。
注意:qsort函数的第四个参数是一个函数指针,其参数为void*类型,不能运算,根据需要进行迁至类型转换。
2.运用
(1)对整型排序
int cmp_int(const void* p1, const void* p2)
{
if (*(int*)p1 > *(int*)p2)//强制类型转换成int*(4个字节,一个元素占4个字节)整形指针解引用,访问4个字节
{ //就是说*(int*)p1代表了一个整型元素进行比较
return 1;
}
else
{
if (*(int*)p1 < *(int*)p2)
return -1;
else
return 0;
}
}
void test1()
{
int arr[] = { 3,1,4,6,5,8,9,2,7,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
print(arr, sz);
}
int main()
{
test1();//用qsort()来排整型数组;
return 0;
}
改进:对第四个参数的返回值进行改进(用表达式)
//第二中表示方法
//int cmp_int(const void* p1, const void* p2)
//{
// return *(int*)p1 - *(int*)p2;
//}
//
(2)结构体排序,结构体中的种类繁多,有字符,有整型,所以我们可以通过两个方面来进行排序,其结果也会不同
- 字符
这里比较字符时需要一个函数是strcmp()(之后我会在字符函数中讲到),strcmp比较对应字符的ASCII码值,如:abcdef和abcdq相比q比e的ascii码值大,所以后者大于前者,同时其返回值也是极其巧妙的符合我们的qsort
struct stu
{
char name[20];
int age;
};
//strcmp()是比较两个字符串的大小,比较对应字符的asc码值
int cmp_char_name(const void* p1, const void* p2)
{
return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);//因为函数参数为void*类型,所以我们应该将其转换为struct stu*类型才能进行比较
//test1()中的是比较的整型元素,所以我们将其转换成了int*类型
}
void test2()
{
struct stu ch[] = {{"zhangsan",20},{"xiaoyan",18},{"wangwu",30}};//创建数组
int sz = sizeof(ch) / sizeof(ch[0]);
qsort(ch, sz, sizeof(ch[0]), cmp_char_name);
}
int main()
{
test2();
return 0;
}
- 数字
struct stu
{
char name[20];
int age;
};
int cmp_char_age(const void* p1, const void* p2)
{
return ((struct stu*)p1)->age - ((struct stu*)p2)->age;//因为函数参数为void*类型,所以我们应该将其转换为struct stu*类型才能进行比较
//test1()中的是比较的整型元素,所以我们将其转换成了int*类型
}
void test3()
{
struct stu ch[] = {{"zhangsan",20},{"xiaoyan",18},{"wangwu",30}};//创建数组
int sz = sizeof(ch) / sizeof(ch[0]);
qsort(ch, sz, sizeof(ch[0]), cmp_char_age);
}
int main()
{
test3();
return 0;
}
(3)模拟实现
我们这里通过排序整型进行模拟,当然后面我们也可以排序其他,这里只是用整型进行试验。
上面的两个代码因为是对整型排序,所以其返回值与qsort差不多。
因为我们是对qsort进行模拟,所以我们之后未来可能排序其他类型数据,因此模拟函数bubble_sort()的参数要类型与qsort高度一致 ,
而这里为什么要通过冒泡排序的形式呢,因为是一对一对字节进行比较的,接下来让小白详细给你们说说
代码是:
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)//判断元素是否需要交换
那么判断完之后肯定要交换,这里我们不能像冒泡排序那样通过在创建一个变量进行交换,因为我们交换的肯定不只是整形数据。所以我们在创建一个函数执行,下面是我的一些思路,大家可以参考,同样如果有好的方法欢迎评论区留言,大家一起进步:
此时我们在创建一个char类型变量放第一个字节,然后以此类推通过循环向后遍历 :
void swap(char* bulf1, char* bulf2, size_t width)
{
int i = 0;
char tem = 0;
for (i = 0; i < width; i++)//因为我们并不知道到底会传来什么样的数据,所以我们选择一个一个字节进行交换,判断
{//所以我们选择char*类型的指针,因为它一次可访问一个字节。
tem = *bulf1;
*bulf1 = *bulf2;
*bulf2 = tem;
bulf1++;//实现一个一个字节访问
bulf2++;
}
}
通过以上分析,大致的模拟实现就结束了,这个模拟不仅可以实现整型的排序,还可以实现其他类型数据排序,下面是整个的代码,大家自行参考:
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void swap(char* bulf1, char* bulf2, size_t width)
{
int i = 0;
char tem = 0;
for (i = 0; i < width; i++)//因为我们并不知道到底会传来什么样的数据,所以我们选择一个一个字节进行交换,判断
{//所以我们选择char*类型的指针,因为它一次可访问一个字节。
tem = *bulf1;
*bulf1 = *bulf2;
*bulf2 = tem;
bulf1++;//实现一个一个字节访问
bulf2++;
}
}
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{
int i = 0;
for (i = 0; i < sz - 1; i++)//i表示趟数
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)//每一趟元素之间的比较和次数
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)//判断元素是否需要交换
{//强制类型转换的原因是base是void类型,不能进行计算
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);//交换元素
}
}
}
}
void test4()
{
int arr[] = { 3,1,4,6,5,8,9,2,7,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
print(arr, sz);
}
int main()
{
test4();//自定义一个函数,模仿qsort()
return 0;
}
到这里今天的学习就结束了,内容很多,希望大家慢慢消化。感谢大家支持。