qsort函数是什么
qsort函数是一种排序函数,他是利用快速排序进行排序的函数,他可以排序任何值,列如整形,浮点型数据等。
提示:以下是本篇文章正文内容
一、qsort函数的使用方法
首先我们要了解qsort函数的定义,在qsort - C++ Reference (cplusplus.com)官网中是这样介绍的
这里这个函数要传这四个参数,第一个是要排序数组里面第一个对象的指针,转换为void*,第二个是数组中base指向的元素个数,类型是一个无符号的整形 ,第三个是大小每个元素的大小,类型为无符号整形。第四个是指向两个要比较元素的指针类型是void*,qsort将反复调用这个函数来比较两个元素,在这个compar函数里面我们获取了这个两个元素的指针 ,如果我们要排序的是整形数据那么就要把void*指针强制转换为int*,然后在解引用就像这样;
int cmp_int(const void* p1,const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
两个相同类型的指针做减法,如果大于0两个元素就交换位置,如果小于等于0就不交换位置,在CPP官网也是这样定义的。
好了,了解完这些之后让我们来看看这个qsort函数该具体怎么使用,下面是代码,我们先来排序一个整形数据
# define _CRT_SECURE_NO_WARNINGS
# include<stdio.h>
# include<stdlib.h>
int cmp_int(const void* p1,const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void arr1(int* arr, int sz)
{
qsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1,3,9,7,10,2,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
arr1(arr,sz);
return 0;
}
这里我们放了一个无序数组,然后求他有几个元素,然后进入arr1函数里面去因为我们之是进行排序所以说不用返回任何值,进入arr1函数内部之后我们就调用qsort函数,他需要stdlib.h头文件,然后反复调用cmp_int函数进行两个指针相减,我们这里排序的是整形数据就需要把他强制转换为整型在解引用如果大于零就交换位置,小于等于就不交换,那是因为,如果两数比较,列如这两个数字1和2,2比1大所以相减大于零就需要交换位置,如果等于0就代表两数相等,小于就不用交换位置,做完这些之后我们需要返回一个整形数据所以要用整形来接受。
最后就是打印数据了,然后结束。
同样我们也可以来排序结构体,我们按照名字大小来进行排序,我们只需要改几个地方就可以了,下面是代码
# define _CRT_SECURE_NO_WARNINGS
# include<stdio.h>
# include<stdlib.h>
# include<string.h>
struct student
{
char name[20];
int age;
};
cmp_student_by_name(const void* p1,const void* p2)
{
return strcmp(((struct student*)p1)->name,((struct student*)p2)->name);
}
print(struct student* ps,int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s,%d\n",ps[i].name, ps[i].age);
}
}
my_strust()
{
struct student stu[3] = { {"zhangsan",18},{"lisi",20},{"wangwu",35}};
int sz = sizeof(stu) / sizeof(stu[0]);
qsort(stu, sz, sizeof(stu[0]), cmp_student_by_name);
print(stu,sz);
}
int main()
{
my_strust();
return 0;
}
这里用了strcmp函数,这个是比较两个字符串的函数,他们比较的是两个字符串的ascii码的大小
然后把强制转换改为(struct student*)类型就可以了。
二、模拟实现
这里我们用的是冒泡排序法来进行模拟qsort函数,先来看看代码。
# define _CRT_SECURE_NO_WARNINGS
# include<stdio.h>
void exchange(char* exc1,char* exc2,size_t size)
{
char temp = 0;
for (int i = 0; i < size; i++)
{
temp = *exc1;
*exc1 = *exc2;
*exc2 = temp;
exc1++;
exc2++;
}
}
int cmpare(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void bubble_sort(void* arr,size_t sz,size_t size,int(*cmpare)(const void* p1, const void* p2))
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < (sz - 1) - i; j++)
{
if (cmpare((char*)arr + j * size, (char*)arr + (j + 1) * size) > 0)
{
exchange((char*)arr + j * size, (char*)arr + (j + 1) * size, size);
}
}
}
}
void print_arr(int* arr,int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
}
void arr()
{
int arr[] = { 1,4,5,2,3,9,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmpare);
print_arr(arr, sz);
}
int main()
{
arr();
return 0;
}
这里我们模拟的是整形数组的排序,
1.分步讲解
先来看看这个,
void arr()
{
int arr[] = { 1,4,5,2,3,9,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmpare);
print_arr(arr, sz);
}
这里我们也和qsort函数官网介绍的一样传四个参数过去,分别是数组的第一个指针,数组元素个数,每个元素的大小,cmpare比较函数 。
接下来进入冒泡排序,冒泡排序详细介绍请看C语言冒泡排序-CSDN博客。
void bubble_sort(void* arr,size_t sz,size_t size,int(*cmpare)(const void* p1, const void* p2))
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < (sz - 1) - i; j++)
{
if (cmpare((char*)arr + j * size, (char*)arr + (j + 1) * size) > 0)
{
exchange((char*)arr + j * size, (char*)arr + (j + 1) * size, size);
}
}
}
}
以void*来接受数组第一个元素的指针,以无符号整形来解释数组元素个数,单个元素大小,然后一个回调函数cmpare参数是两个void*指针,返回类型是int。
这里就是cmpare比较函数
int cmpare(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
强制转换为int在解引用比较看看这两个元素需不需要交换位置。
如果需要交换就进入这个函数
f (cmpare((char*)arr + j * size, (char*)arr + (j + 1) * size) > 0)
{
exchange((char*)arr + j * size, (char*)arr + (j + 1) * size, size);
}
下面是重点,也就是交换两个元素这个函数里面
让我们来看看为什么要把元素的类型强制转换为char*类型的指针,那是因为我们这个函数要实现的是多种不同数据的类型进行排序,char*指针往后面走一位就是增加一个字节,我们可以拿这一个字节当作一个单位一个基准,,size的意思是单个元素的大小也就是sizeof(arr[0])后面的j*size和(j+1)*size的意思就是在arr数组中第j个元素和j+1个元素,他们为什么要乘一个size呢,那是由于我们这个是要排序任何数据的排序函数,我们不知道单个元素的大小,所以要乘以一个size,最后进入到exchange函数里面。
void exchange(char* exc1,char* exc2,size_t size)
{
char temp = 0;
for (int i = 0; i < size; i++)
{
temp = *exc1;
*exc1 = *exc2;
*exc2 = temp;
exc1++;
exc2++;
}
}
我们以两个指针来接收arr j 和 arr j+1 元素
注意我们这里的比较方式是一个字节一个字节的方式进行比较
下面可以看看这张图
我们这样的目的是因为,我们是以char*为单位的他向后走一次就是一个字节,然后这里是整形数据,size的大小是4个字节,一个整形就变成4个char类型进行交换,当交换次数等于size时就交换成功了,其他类型如double,float数据类型同理
当交换完成的时候最后就是打印了
void print_arr(int* arr,int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
}
运行结果为
至此程序结束
同样的原理也可以用于结构体的排序,下面是一个结构体我们以名字大小进行排序
# define _CRT_SECURE_NO_WARNINGS
# include<stdio.h>
# include<stdlib.h>
# include<string.h>
struct student
{
char name[20];
int age;
};
int compare(const void*p1,const void*p2)
{
return strcmp(((struct student*)p1)->name,((struct student*)p2)->name);
}
void swap(char* a1,char*a2,size_t size)
{
for (int i = 0; i < size; i++)
{
char temp = *a1;
*a1 = *a2;
*a2 = temp;
a1++;
a2++;
}
}
void bullu_sort(const void* arr, size_t sz, size_t size, int(*compare)(const void* p1, const void* p2))
{
for (int i = 0; i < sz; i++)
{
for (int j = 0; j < sz; j++)
{
if (compare((char*)arr + j * size,(char*)arr + (j + 1) * size) > 0)
{
swap((char*)arr+j*size,(char*)arr+(j+1)*size,size);
}
}
}
}
void print(size_t sz,struct student* p3)
{
for (int i = 0; i < sz; i++)
{
printf("%d,%s \n", p3[i].age, p3[i].name);
}
}
void my_struct()
{
struct student arr[3] = { {"lisi",36},{"wangwu",20},{"zhangsan",21} };
int sz = sizeof(arr) / sizeof(arr[0]);
bullu_sort(arr, sz, sizeof(arr[0]), compare);
print(sz,arr);
}
int main()
{
my_struct();
return 0;
}
运行结果为
总结
总的来说qsort函数慢慢理解之后还是很简单的