1 qsort原理介绍
函数原型:
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
base – 指向要排序的数组的第一个元素的指针。
nitems – 由 base 指向的数组中元素的个数。
size – 数组中每个元素的大小,以字节为单位。
compar – 用来比较两个元素的函数。
注意cmp这个函数指针,返回值类型必须是int,参数是两个const void *,因此在写cmp函数时,可以选择在函数体内,再将a,b强转为所需要的类型;
我们以一个int数组为例,现在要将这个数组按照元素大小,升序排列。cmp函数与参数a,b对于qsort的影响是:
if a>b return positive
if a=b return 0
if a<b return negative
按照这个要求设计的return就会使得按照升序排序,注意是按照这个要求去设计所需要的返回值!只要能保住a,b在你设计的规则下,产生这样的返回值即可。而这三个条件刚好与a-b等价:
a>b return positive ~ a-b
a=b return 0 ~ a-b
a<b return negative ~ a-b
所以cmp函数可以写为:
int cmp(const void *a,const void *b){
return *(int*)a-*(int*)b;/*按照升序排序*/ //降序就是 *(int*)b-*(int*)a;
}
strcmp函数,也就是把字符串str1和str2从首字符开始逐个字符的进行比较,直到某个字符不相同或者其中一个字符串比较完毕才停止比较。
int strcmp(char *str1,char * str2);
那么可以等价如下:
a>b return positive ~ a-b strcmp(a, b)//若字符串a大于字符串b,返回结果大于零;
a=b return 0 ~ a-b strcmp(a, b)//若字符串a等于字符串b,返回结果等于零;
a<b return negative ~ a-b strcmp(a, b)//若字符串a小于字符串b,返回结果小于零;
strcmp函数返回值正与我们所要求的升序设计一致,所以字符串操作cmp函数可写为:
int cmp(const void *a, const void *b) {
return strcmp((char * )a, (char *)b);
}
2 qsort使用举例
2.1 一维数组排序
// 一维排序
int cmp1(const void *a, const void *b)
{
int *x = (int *)a;
int *y = (int *)b;
return (*x) - (*y); // 升序,降序改为 (*y) - (*x)
}
// 一维排序
int arr[] = {9, 13, 12, -1, -1, 2, 8};
int arrLen = sizeof(arr) /sizeof(arr[0]);
qsort(arr, arrLen, sizeof(arr[0]), cmp1);
//排序结果arr1: -1 -1 2 8 9 12 13
2.2 二维数组排序
// 二维排序
int cmp2(const void *a, const void *b)
{
int *x = (int *)a;
int *y = (int *)b;
return x[0] - y[0]; // 按照特性一升序, 按照特性二升序为 x[1] - y[1]
}
// 二维排序
int arr2[3][2] = {{7,6},{2,4},{1,2}}; // 三组数据:每组数据两个特性
qsort(arr2, 3, sizeof(arr2[0]), cmp2);
/*排序结果
arr2
1 2
2 4
7 6
*/
2.3 二维数组排序升级
// 二维排序
int cmp3(const void *a, const void *b)
{
int *x = (int *)a;
int *y = (int *)b;
if (x[0] == y[0]) { // 特性一相同,按照特性二进行升序
return x[1] - y[1];
}
return x[0] - y[0]; // 按照特性一升序
}
// 二维排序
int arr3[3][2] = {{1,6},{2,4},{1,2}}; // 三组数据:每组数据两个特性
qsort(arr3, 3, sizeof(arr3[0]), cmp3);
/*排序结果
arr3
1 2
1 6
2 4
*/
2.4 二维数组指针排序
//二维数组指针排序(注意)
int cmp4(const void **a, const void **b)
{
int *x = (int *)(*a); // 注意区别!!
int *y = (int *)(*b);
if (y[1] == x[1]) { // 特性二相同,按照特性1进行降序
return y[0] - x[0];
}
return y[1] - x[1]; // 按照特性二降序
}
//二维数组指针排序
int **arr4 = (int **)malloc(sizeof(int *)* 3); // 三组数据:每组数据两个特性
for (i = 0; i < 3; i++) {
arr4[i] = (int *)malloc(sizeof(int) * 2);
for (j = 0; j < 2; j++) {
arr4[i][j] = i + j;
}
}
qsort(arr4, 3, sizeof(arr4[0]), cmp4);
/*排序结果
arr4
2 3
1 2
0 1
*/
2.5 字符串排序
//字符串数组排序
int cmp5(const void *a, const void *b)
{
char *x = *(char **)a;
char *y = *(char **)b;
return strcmp(x, y);
}
char *strs[3] = {"shn", "abc", "axy"};
qsort(strs, 3, sizeof(char *), cmp5);
/*排序结果
strs:
abc axy shn
*/
2.6 结构体中使用qsort排序
typedef struct arrInfo{
int data;
int index;
} ArrInfo;
//结构体变量排序
int cmp6(const void *a, const void *b)
{
ArrInfo *x = (ArrInfo *)a;
ArrInfo *y = (ArrInfo *)b;
if (y->data == x->data) { // 特性一data相同,按照特性二index进行降序
return y->index - x->index;
}
return y->data - x->data; // 按照特性一data降序
}
//结构体中字段排序
ArrInfo *arr6 = (ArrInfo *)malloc(sizeof(ArrInfo)* 3); // 三组数据:每组数据两个特性
for (i = 0; i < 3; i++) {
arr6[i].data = i;
arr6[i].index = i + 1;
}
qsort(arr6, 3, sizeof(ArrInfo), cmp6);
/*排序结果
arr6
2 3
1 2
0 1
*/
2.7 结构体多级排序
根据学生信息,按照身高降序,身高相同时按照体重降序,体重也相同时按照姓名进行升序;
typedef struct StuInfo{
char name[20];
int high;
int weight;
} StudentInfo;
//学生信息排序
int cmp7(const void *a, const void *b)
{
StudentInfo *x = (StudentInfo *)a;
StudentInfo *y = (StudentInfo *)b;
if (y->high== x->hight) { // 身高相同,按照体重进行降序
if (y->weight == x->weight) { // 体重相同,按照姓名首字母进行升序
return strcmp(x->name, y->name);
}
return y->weight - x->weight;
}
return y->high- x->high; // 按照身高降序
}
//学生信息排序
StudentInfo arr7[5] = {{"shn", 170, 65},
{"xz",175, 80},
{"axz",170, 65},
{"dxz",175, 90},
{"we",170, 60},}; // 5个学生
qsort(arr7, 5, sizeof(StudentInfo), cmp7);
/*排序结果
arr7
dxz 175 90
xz 175 80
axz 170 65
shn 170 65
we 170 60
*/
为了加强对qsort使用理解,可以练习力扣937题。