qsort使用前提
- 首先包含头文件:#include <stdlib.h>
- 其次需要cmp比较函数:(自己编)
qsort函数介绍
- 函数原型:
- void qsort(void * base, size_t nitems, size_t size, int ( * compar)(const void * , const void * ))
- 对base所指的数组进行排序。
- 参数解释:
- base: 指向要排序的数组的第一个元素的指针。
- nitems: 由 base 指向的数组中元素的个数。
- size: 数组中每个元素的大小,以字节为单位。
- compar: 用来比较两个元素的函数,即函数指针(回调函数)
- qsort函数(全称quicksort)是ANSI C标准中提供的,声明在stdlib.h文件中,是根据二分法写的,其时间复杂度为n*log(n)。
- 举一个栗子:
qsort(array,n,sizeof(int),comp);
cmp比较函数
- cmp函数的介绍
- compar参数指向一个比较两个元素的函数。比较函数的原型应该像下面这样。注意两个形参必须是const void* 型,同时在调用compar 函数(compar实质为函数指针,这里称它所指向的函数也为compar)时,传入的实参也必须转换成const void* 型。在compar函数内部会将const void *型转换成实际类型。
- 该函数不返回任何值。
- 函数原型:
- int compar(const void *p1, const void *p2);
- 默认升序排列(从小到大),如果想降序排列返回 * b- * a即可。
- 返回值解释:
- 如果compar返回值小于0(<0),那么p1所指向元素会被排在p2所指向元素的左面;
- 如果compar返回值等于0(=0),那么p1所指向元素与p2所指向元素的顺序不确定;
- 如果compar返回值大于0(>0),那么p1所指向元素会被排在p2所指向元素的右面。
cmp不同的应用场景
一维数组
- 下面是由小到大排序,return *(int *)b - *(int *)a; 为由大到小排序。
- 参数列表是两个空指针,现在他要去指向你的数组元素。所以转换为你当前的类型,然后取值。
- 默认升序排列(从小到大),如果想降序排列返回 * b- * a即可。
对int和char类型的排序:
- return * (int*)a - * (int*)b ;
- return * (char*)a - * (char*)b ;
- 程序如下:
//写法一:
int cmp (const void * a, const void * b)
{
return *(int*)a - *(int*)b ;
//return * (char*)a - * (char*)b ;
}
//写法二:
int cmp(const void* _a , const void* _b) //参数格式固定
{
int* a = (int*)_a; //强制类型转换
int* b = (int*)_b;
// int* a = (char*)_a; //强制类型转换
//int* b = (char*)_b;
return *a - *b;
}
对double类型数组的排序:
- return * (double*)a>* (double*)b ? 1 : -1;
- 在对浮点或者double型的一定要用三目运算符,因为要是使用像整型那样相减的话,如果是两个很接近的数则可能返回一个很小的小数(大于-1,小于1),而cmp的返回值是int型,因此会将这个小数返回0,系统认为是相等,失去了本来存在的大小关系。
- 程序如下:
double in[100];
int cmp_double(const void* _a , const void* _b) //参数格式固定
{
double* a = (double*)_a; //强制类型转换
double* b = (double*)_b;
return *a > *b ? 1 : -1; //特别注意
}
对字符串进行排序:
- 程序如下:
char word[100][10];
int cmp_string(const void* _a , const void* _b) //参数格式固定
{
char* a = (char*)_a; //强制类型转换
char* b = (char*)_b;
return strcmp(a,b);
}
qsort(word,100,sizeof(word[0]),cmp_string);
- 接着在主函数中使用qsort(nums,numsSize,sizeof(int),cmp); 进行升序排序操作。
二维数组
- 输入一个二维数组:int a[1000][2];
- 其中按照a[0]的大小进行一个整体的排序,其中a[1]必须和a[0]一起移动交换。(即第一行和第二行(a[0]和a[1]分别代表第一行和第二行的首地址)。)
- 使用库函数排序的代码量并不比用冒泡排序法小,但速度却快很多。
- 因为是二维数组,所以在一个维度相等的时候,比较另一个维度的大小。即下面程序中的:
- else return ((int * )a)[1] - ((int * )b)[1];
qsort(a,1000,sizeof(int)*2,comp);
int comp(const void*a,const void*b)
{
return((int*)a)[0]-((int*)b)[0];
}
- 函数指针定义(对于非malloc申请的多维数组):
int cmp(const void *a, const void *b) {
if (((int *)a)[0] != ((int *)b)[0])
return ((int *)a)[0] - ((int *)b)[0];
else return ((int *)a)[1] - ((int *)b)[1];
}
- 函数指针定义(对于malloc申请的多维数组):
int cmp(const void *a, const void *b)
{
int *_a = *(int **)a;
int *_b = *(int **)b;
if (_a[0] != _b[0]) return _a[0] - _b[0];
else return _a[1] - _b[1];
}
- 仅对二维数组某行排序:
int b_arr[][3] = { {3, 2, 5},
{1, 1, 8},
{6, 2, 9},
{9, 2, 4} };
//仅对第1行排序,修改如下:
//cmp2中
((int *)a)[0] - ((int *)b)[0]; //直接比较当前传入的二维数组所对应的值
//qsort中
qsort(b_arr, 3, 1 * sizeof(int), cmp2);
//等价于
qsort(&(b_arr[0][0]), 3, 1 * sizeof(int), cmp2);
//传入第1行第1列首地址,只比较3个元素,元素相关空间只有1int大小即4byte。
//仅对第2行排序,修改如下:
//cmp2中
((int *)a)[0] - ((int *)b)[0]
//qsort中
qsort(&b_arr[1][0], 3, 1 * sizeof(int), cmp2);
//传入第2行第1列首地址,只比较3个元素,元素相关空间只有1int大小即4byte。所以只比较第2行开始的3个int数据。
//仅对第3行排序,修改如下:
//cmp2中
((int *)a)[0] - ((int *)b)[0]
//qsort中,
qsort(&b_arr[2][0], 3, 1 * sizeof(int), cmp2);
- 按某列大小来排各行的序:
- 按某列的元素大小来排各行的序,内部连续存储,qsort实现只是根据某列元素比较结果,将该行元素从行首开始批量挪固定长度的数据交换。
- 利用快排的入参中,修改比较函数,由于二维数组存储是连续的,所以可以仅比较某列的元素来实现。
//按第1列的元素大小排序,将各行调整为1列的大小升序排列,那需要整体移动一行的数据,大小即3 * sizeof(int)。
//qsort函数一直为:
qsort(b_arr, 3, 3* sizeof(int), cmp2);
//cmp2中
((int *)a)[0] - ((int *)b)[0]
//a为某一行的首地址,向后偏移0位,用[0]还有解引用的功能,即取a[i][0]
//按第2列的元素大小排序
((int *)a)[1] - ((int *)b)[1]
//a为某一行的首地址,向后偏移1位,即取a[i][1]
//按第3列的元素大小排序
((int *)a)[2] - ((int *)b)[2]
- 按某行大小来排各列的序
- 如果需要按某行的元素来排各列的序,因为在内部存储时不是连续的,最直接的方式是将数组行列互换然后再排序,之后再转回来。
- 最后在主程序中使用qsort(nums,numsSize, sieof(int *), cmp);
结构体
- 很多时候我们都会对结构体排序,一般这个时候都在cmp函数里面先强制转换了类型,不要在return里面转换,这样程序会更清晰。
对结构体中某个关键字排序(对结构体一级排序):
//写法一:
struct node
{
double data;
int no;
} s[100];
int i,n;
int cmp(const void *a,const void *b)
{
struct node *aa=(node *)a;
struct node *bb=(node *)b;
return(((aa->data)>(bb->data))?1:-1);
}
//写法二:
structNode
{
double data;
int other;
}s[100];
int Comp(constvoid*p1,constvoid*p2)
{
return(*(Node*)p2).data>(*(Node*)p1).data?1:-1;
}
qsort(s,100,sizeof(s[0]),Comp);
按结构体中多个关键字排序(对结构体多级排序)[以二级为例]:
//写法一:
struct node
{
double data;
int no;
} s[100];
int i,n;
int cmp(const void *a,const void *b)
{
struct node *aa=(node *)a;
struct node *bb=(node *)b;
if(aa->data!=bb->data)
return(((aa->data)>(bb->data))?1:-1);
else
return((aa->no)-(bb->no));
}
//写法二:
struct Node
{
int x;
int y;
}s[100];
//按照x从小到大排序,当x相等时按y从大到小排序
int Comp(const void*p1,const void*p2)
{
struct Node*c=(Node*)p1;
struct Node*d=(Node*)p2;
if(c->x!=d->x) return c->x - d->x;
else return d->y - c->y;
}
对结构体中字符串进行排序:
//写法一:
int cmp(const void *a,const void *b)
{
struct node *aa=(node *)a;
struct node *bb=(node *)b;
if(aa->data!=bb->data)
return(((aa->data)>(bb->data))?1:-1);
else
return((aa->no)-(bb->no));
else
return strcmp(aa.str,bb.str);
//return strcmp(aa->str,bb->str);
//按照结构体中字符串str的字典顺序排序
}
//写法二:
struct Node
{
int data;
char str[100];
}s[100];
//按照结构体中字符串str的字典序排序
int Comp(const void*p1,const void*p2)
{
return strcmp((*(Node*)p1).str,(*(Node*)p2).str);
}
qsort(s,100,sizeof(s[0]),Comp);
例程
//一维数组类型
#include <stdio.h>
#include<stdlib.h>
int jobs[] = {3,2,3,7,1,2,5}, jobsSize = 7;
int cmp (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int main(){
qsort(jobs, jobsSize, sizeof(int), cmp);
printf("%d %d %d %d %d %d %d\n",jobs[0],jobs[1],jobs[2],jobs[3],jobs[4],jobs[5],jobs[6]);
}
//二维数组类型(对于非malloc申请的多维数组)
int cmp_intTwo(const void *a, const void *b) {
if (((int *)a)[0] != ((int *)b)[0])
return ((int *)a)[0] - ((int *)b)[0];
else return ((int *)a)[1] - ((int *)b)[1];
}
int main()
{
int arr[5][2] = {{2, 1}, {1, 2}, {2, 2}, {1, 1}, {1, 3}};
qsort(arr, 5, sizeof(arr[0]), cmp_intTwo);
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 2; ++j)
printf("%d ", arr[i][j]);
printf("\n");
}
return 0;
}
//二维数组类型(对于malloc申请的多维数组)
int cmp_intTwoExtend(const void *a, const void *b)
{
int *_a = *(int **)a;
int *_b = *(int **)b;
if (_a[0] != _b[0]) return _a[0] - _b[0];
else return _a[1] - _b[1];
}
int main()
{
int row = 3;
int **arr1 = (int **) malloc(sizeof(int *) * row);
for (int i = 0; i < 3; ++i) {
arr1[i] = (int *) malloc(sizeof(int) * 2);
}
arr1[0][0] = 1, arr1[0][1] = 2;
arr1[1][0] = 1, arr1[1][1] = 1;
arr1[2][0] = 0, arr1[2][1] = 2;
qsort(arr1, 3, sizeof(arr1[0]), cmp_intTwoExtend);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 2; ++j)
printf("%d ", arr1[i][j]);
printf("\n");
}
for (int i = 0; i < 3; ++i)
free(arr1[i]);
free(arr1);
return 0;
}
参考文献:
C 库函数 - qsort()
C语言qsort函数用法
C语言中qsort()库函数的排序应用
qsort百度百科
qsort用法
C学习:qsort快排函数在二维数组中的灵活应用