在一些编程题中经常需要你按照某个指标按照从小到大或从大到小输出一些数据,这时你可以自己写一个排序函数进行排序,但是其实C语言函数库中就有一个排序函数–qsort函数,使用它可以节省你单独写排序函数所耗费的时间,因而在一些比赛中广泛应用。
qsort函数用白一点的话来讲就是可以排任何类型的数,不仅仅局限于整形
它要用到的头文件
下面是它的函数参数介绍
void qsort ( void * base, //base:参与排序的数组指针
size_t num, //num:参与排序的元素个数;
size_t size, //size:单个元素的大小;
int ( * comparator ) ( const void *, const void * ) )
//( * comparator ) ( const void *, const void * ):比较函数指针;
*base:参与排序的数组指针;
num:参与排序的元素个数;
size:单个元素的大小,单位是字节;
( * comparator ) ( const void , const void * ):比较函数指针(程序员自己定义);
我们来一个实际列子使用一下qsort函数>>
#include
#include
struct Stu
{
char name[20];
int age;
};
int sort_by_age(const void* e1, const void* e2)//用来比较age大小的函数,这里就相当于qsort中的比较函数
{
//如果age1>age2就返回大于零,相等就返回零,小于就返回小于零的数即可
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main()
{
//使用qsort函数排序结构体数据
struct Stu s[3] = { {"zhangsan", 30},{"lisi", 34},{"wangwu", 20} };
int sz = sizeof(s) / sizeof(s[0]);
//按照年龄来排序
qsort(s, sz, sizeof(s[0]), sort_by_age);
for (int i = 0; i < 3; i++)
{
printf(" %s %d \n",s[i].name, s[i].age);
}
return 0;
}
运行后的结果>
当然如果你想用名字排名也可以,只需把这个比较函数传给qsort即可
int sort_by_name(const void*e1, const void*e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
冒泡排序的原理是:从左到右,相邻元素进行比较。每次比较一轮,就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。
以从小到大排序为例,第一轮比较后,所有数中最大的那个数就会浮到最右边;第二轮比较后,所有数中第二大的那个数就会浮到倒数第二个位置……就这样一轮一轮地比较,最后实现从小到大排序。
它的时间复杂度是o(N^2)
假如有一个数组为(21435)
这个也比较简单我就直接上代码了>>
void BBsort(int arr[], int size)
{
int j,i,tem;
for (i = 0; i < size-1;i ++)//size-1是因为不用与自己比较,所以比的数就少一个
{
int count = 0;
for (j = 0; j < size-1 - i; j++) //size-1-i是因为每一趟就会少一个数比较
{
if (arr[j] > arr[j+1])//这是升序排法,前一个数和后一个数比较,如果前数大则与后一个数换位置
{
tem = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tem;
count = 1;
}
}
if (count == 0) //如果某一趟没有交换位置,则说明已经排好序,直接退出循环
break;
}
这里有一个我上一期的冒泡排序,可以看看C语言—冒泡排序
⭐️首先我们要想qsort为什么能排序任意类型的数据?
✨我们从它的函数参数或许可以知道些许qsort
void qsort ( void * base, //base:参与排序的数组指针
size_t num, //num:参与排序的元素个数;
size_t size, //size:单个元素的大小;
int ( * comparator ) ( const void *, const void * ) )
//( * comparator ) ( const void *, const void * ):比较函数指针;
这里就直接代码展示>>
代码往后有解释为什么,或者有什么不懂希望你们可以跟我交流。
//qsore函数使用
//模仿qsort实现一个冒泡排序的通用算法
#include
#include
void Swap(char* buff1, char* buff2, int width);//交换元素的函数声明
//base数组首地址,sz数组元素长度,width元素占多少个字节,cmp元素比较的方式的函数,元素相等return 0,大于return 大于零的数,小于则return小于零的数
void bubble_sort(void *base,int sz,int width,int (*cmp)(const void* e1,const void* e2))
{
int i, j;
for (i = 0; i < sz; i++)
{ //一趟的排序
for (j = 0; j < sz - i - 1; j++)
{
//两个元素比较
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
}
int cmp(const void* e1,const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[] = { 1,3,4,2,6,5,7,8,9,10 };
qsort(arr, sizeof(arr) / sizeof(arr[0]), 4, cmp);
//bubble_sort(arr, sizeof(arr) / sizeof(arr[0]), 4, cmp);
for (int j = 0; j < 10; j++)
{
printf("%d ", arr[j]);
}
return 0;
}
void Swap(char* buff1, char* buff2, int width)//交换元素的函数
{
int i;
for (i = 0; i < width; i++)
{
char tem = *buff1;
*buff1 = *buff2;
*buff2 = tem;
buff1++;
buff2++;
}
}
这些是我从上面拿出来重点要跟大家说明的
void*base
☁️首先为什么是void* 类型,可能有些小伙伴不知道void* 的用法,我这里简单说一下,void* 确实它跟int* char* 等等都是指针,而像int* 这些是只能接收一下int* 类型的指针,void* 就不一样了,它可以接收所有的指针类型
这就很好的解释了为什么要用void* 来作为函数参数了,因为我们是要排序任意类型的数据,所以一定要用void* 来接收
比较一下通用冒泡排序和一般冒泡排序的判断元素大小
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
if (arr[j] > arr[j+1])//一般的冒泡排序
☁️而这里就体现了为什么要传每一个元素的大小了,因为是接收任意类型的数据,所以我们需要通过每一元素的大小来确定该用多少字节来进行比较和元素交换,而且这里base要强制类型转换,因为void*
是不能直接访问的,我们把它按char*来处理后,就很容易跟后面传进来的“width”元素大小进行匹配
....
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
......
void Swap(char* buff1, char* buff2, int width)//交换元素的函数
{
int i;
for (i = 0; i < width; i++)
{
char tem = *buff1;
*buff1 = *buff2;
*buff2 = tem;
buff1++;
buff2++;
}
}
☁️这是一个交换函数,这里可以是void* 接收,也可以提前将base强制类型转换成char* 然后再传,我这就是后者。这我要跟大家说的就是为什么要传“width”元素大小,因为跟前面一样,我们知道了元素大小才能知道要交换多少个元素
本人也是初学者,难免出现一些错误的地方,欢迎各位帮忙大佬指正。