qsort()
函数原型:void qsort(void*, size_t, size_t, int (*)(const void*, const void*) );
qsort,全称应该是quicksort 即快速排序(排序时间复杂度 n* log(n))。
简单概括就是 系统写好的排序函数,不需要自己去写复杂的排序算法,调用一下,数组就排好序了。
显而易见,该函数接受三个参数:
1:void* 待排序数组,排序之后的结果仍放在这个数组中
2:size_t 数组中待排序元素个数
3:size_t 各元素的占用空间大小(单位为字节)
4: int (*)(const void*, const void*) 指向函数的指针,也就是比较函数(关键)。比较函数也就是要告诉这个函数,你给的数组要按何种方式进行排序。
前三个参数没啥好说的,每次按照要求填写就好了。有意思的是最后一个参数。比较函数。通常会命名为cmp。这份代码中命名为比较sortingData,个人觉得并不是一个很好的名字,不如改成cmpData更加好理解。
那么这玩意怎么用呢。
举个栗子。
假如现在有整型数组 a[] = {2,4,3,5,1};
那么对这个数组排序,显然是要需要比较 两个整数值之间 的大小。
那么cmp函数写成如下形式:
int cmp(const void* a , const void* b)
{
int* a_ = (int*)a; //强制类型转换
int* b_ = (int*)b;
return *a_ - *b_;
}
运行效果:
假如现在有字符型数组 a[] = {‘2’,‘4’,‘3’,‘5’,‘1’};
那么对这个数组排序,显然是要需要比较 两个字符值之间 的大小。
那么cmp函数写成如下形式:
int cmp(const void* a , const void* b)
{
char* a_ = (char*)a; //强制类型转换
char* b_ = (char*)b;
return *a_ - *b_;
}
运行效果:
可以发现的是,cmp函数的参数列表是不变的:
int cmp(const void* a , const void* b)
这玩意就是比较两个元素的大小,如果左边比右边大,返回正数,反之,返回负数,如果相等,显然返回0。
任何一个排序算法都要比较大小吧。就拿冒泡排序来说:
for(int i = 0 ; i < n ; i ++){
for(int j = 1 ; j < n-i ; j ++){
if(a[j-1] > a[j]){ // 这玩意 就相当于 cmp 函数 用来比较大小的
swap(a[j-1],a[j]); // swap(x,y) 就是交换x,y的值
}
}
}
那么如果要从大到小排序数组呢,只要把
return *a_ - *b_;
改成:
return *b_ - *a_;
就完事了。
以上就是 qsort+cmp 函数的朴素用法。
但是,如果我要排序一个结构体呢。
比如某次考试,一个结构体记录了学生成绩和学号,那么需求是先按成绩从小到大排序,如果成绩相同,则按学号从小到大排序。
于是有以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
int score; // 成绩
int id; // 学号
}Stu;
int cmp(void* a,void* b){
Stu* a_ = (Stu*)a;
Stu* b_ = (Stu*)b;
if(a_->score < b_->score){
return -1;
}
if(a_->score > b_->score){
return 1;
}
if(a_->id < b_->id){
return -1;
}
if(a_->id > b_->id){
return 1;
}
}
int main()
{
Stu stu[3];
stu[0].id = 201802; stu[0].score = 60;
stu[1].id = 201800; stu[1].score = 80;
stu[2].id = 201801; stu[2].score = 60;
qsort(stu,3,sizeof(Stu),cmp);
for(int i = 0 ; i < 3 ; i ++){
printf("%d: %d\n",stu[i].id,stu[i].score);
}
return 0;
}
或者cmp函数这么写(效果是一毛一样的):
int cmp(void* a,void* b){
Stu* a_ = (Stu*)a;
Stu* b_ = (Stu*)b;
if(a_->score == b_->score){ // 如果相等则按id排序
return a_->id - b_->id;
}
return a_->score - b_->score; // 负责按成绩排
}
看看效果:
比如再把id改成字符串呢,当然没问题,把id比较的地方改成字符串比较:
int cmp(void* a,void* b){
Stu* a_ = (Stu*)a;
Stu* b_ = (Stu*)b;
return strcmp(a_->id,b_->id); // strcmp() 会返回字符串比较结果 正负值 或者 0
}
至此,你应该已经掌握了qsort+cmp的用法。
最后一个思考题,如果要按 (成绩*100+学号*3)的奇偶性排序,如果奇偶性相同按(成绩*100+学号*3) 的值排序,如果还相等,那么按成绩,最后按学号。cmp应该怎么写。