目录
前言
这篇文章就是指针进阶的收尾环节了,相信看过C语言进阶——指针(下)的小伙伴一定还记着文章末尾的回调函数吧。这篇文章就是借qsort函数的模拟实现来给小伙伴们展示一下回调函数的运用。
1.冒泡排序的实现
在实现qsort函数,相信有的小伙伴对冒泡排序还存有疑惑,甚至是第一次接触这个名词,那么我们就先来讲讲冒泡排序。
1.1冒泡排序的概念
“冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
相信聪明的你已经知道了冒泡排序是一种依次比较相邻元素并按照相应规则进行排序的排序方法。
在这种排序过程中,重要的元素共有两个,分别是冒泡排序的趟数以及比较的次数。一般而言,趟数是元素的个数减一得到,为什么不等于元素的个数呢?因为每进行一趟冒泡排序,就会有一个元素来到它正确的位置,所以当除了最后一个元素以外的其他元素都来到了正确位置的时候,那么最后一个元素也一定处于正确位置。
说完趟数,再来聊聊比较次数。有亲自实践的小伙伴们一定发现了在进行第一趟冒泡排序的时候,比较的次数是最多的,是除了第一个元素以外的所有元素都要与之比较,也就是元素的个数减一,但是随着趟数的增加,比较的次数也会随之减少,究其原因,是因为每经过一次冒泡排序,就会有一个元素来到正确的位置,那么这个正确的元素也就无需参加后续的比较了。
那么具体的代码实现究竟是怎么样呢?接下来就让我们一起揭开它神秘的面纱,一探究竟!
1.2具体代码的实现
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;
}
}
}
}
2.qsort函数
在了解完冒泡排序之后,有的小伙伴还是第一次见到qsort函数,那么下面我们就来简单介绍一下qsort函数。
首先我们要清楚的是qsort函数是库里面的函数,所以我们在使用的时候要引用头文件<stdlib.h>。
然后我们再来了解一下这个函数的各个部分。第一个是返回类型,从上图中我们可以看到返回类型是void,也就是说当我们在引用这个函数的时候,它并不会返回任何参数。第二个是参数部分,第一个参数是一个指针变量,第二、三个参数是无符号的整型变量,第四个就是我们之前讲解过的函数指针变量。在第四个参数部分也就体现了回调函数这一功能。
最后我们来讲讲这个函数该怎么使用。在引用完头文件后,我们下一步就是要对这个函数进行传参,在意义上分别是要排序的对象,元素个数,元素大小(以字节为单位),判断是否交换的函数(根据需求自写)。
说到第四个参数所指向的函数需要根据需求自己来写,可能有小伙伴就疑惑了,到底要怎么写呢。别怕,上面这幅图片可以说是为我们函数的书写提供了方向。首先你要确保你书写的函数的返回类型为int,并且依据交换的原则来进行返回值的代码书写。拿整型数组排序为例,如果你想要最终数组的整形呈现升序排序,那么如果两个数是降序排序就应该返回小于0的整型。具体代码的实现如下,以整形数组和结构体为例。
//用qsort函数实现各种类型的排序
//整形数组的排序
int cmp_int(void const* e1, void const* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int i = 0;
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, 4, cmp_int);
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
//结构体的排序
struct stu
{
char name[20];
int age;
};
int cmp_age(void const*e1, void const*e2)
{
return (((struct stu*)e1)->age - ((struct stu*)e2)->age);
}
int cmp_name(void const* e1, void const* e2)
{
return strcmp(((struct stu*)e1)->name ,((struct stu*)e2)->name);
}
int main()
{
struct stu Stu[3] = { {"zhangsan", 20},{"wangwu", 42},{"lisi",29}};
int sz = sizeof(Stu) / sizeof(Stu[0]);
//qsort(Stu, sz, sizeof(Stu[0]), cmp_age);
qsort(Stu, sz, sizeof(Stu[0]), cmp_name);
return 0;
}
3.qsort函数的实现
随着我们学习的不断深入,类型的不断丰富,我们会发现上面的冒泡排序已然满足不了我们的需求,诸如结构体的排序,这种代码已经是无能为力了。那么接下来我们就自己来利用冒泡排序的原理来书写qsort函数。
//实现元素的交换
void swap(char* e1, char* e2, size_t sz)
{
size_t i = 0;
for (i = 0; i < sz; i++)
{
char tmp = 0;
tmp = *e1;
*e1 = *e2;
*e2 = tmp;
e1++;
e2++;
}
}
//qsort函数的自定义
void bubble_sort(void* base, size_t num, size_t size, int (*compar)(const void*e1, const void*e2))
{
size_t i = 0;
size_t j = 0;
//冒泡排序的趟数
for (i = 0; i < num-1; i++)
{
//比较的次数
for (j = 0; j < num - i - 1; j++)
{
if (compar((char *)base+j*size,(char*)base+(j+1)*size)>0)
{
//交换
swap((char*)base + j*size, (char*)base + (j + 1)*size, size);
}
}
}
}
总结
码文不易,希望各位大佬们在看完文章后,能够动动小手指点点赞,走走关注,多多评论。让我们共同进步,奔赴美好的明天!