NOIP学习小站
西安交通大学附属中学航天学校

sort函数对结构体数组排序

结构体数组是不能直接简单调用sort函数排序,因为两个结构体变量不能直接比较大小(没有定义比较规则),但是可以指定sort函数的第三个参数(比较函数)来实现对结构体数组的自定义排序。

【问题】一次测试后,小组内\(n\)位同学的姓名和语文、数学、英语成绩如下(第一行是\(n\)的值):

6
Rose 80 90 100
Jack 78 89 97
Lily 88 89 90
Tom 66 68 87
Frank 88 90 89
Jerry 89 90 91

现在要将所有同学按照总分降序排序后再输出所有信息。为了方便操作,可以将考试成绩信息定义为结构体,里面有姓名和语文、数学、英语、总分成绩这些成员变量,所有同学的信息存放到结构体数组中即可。最后可以使用排序函数来实现结构体数组的自定义排序:

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 50;		//最多50位学生
//将一位学生的多项信息“捆绑”成一个结构体 
struct Student{
	string name;	   //学生姓名
	int yw,sx,yy;		//考试成绩
	int zf; 
};
Student stu[N+1]; 

//供sort函数使用的比较函数:按照总分降序排序
//参数使用const Student&类型和Student类型均可,不过前者执行效率更高 
bool cmp(const Student &a,const Student &b){
	return a.zf>b.zf;
}

//输出结构体数组信息(\t是制表符,产生tab键效果) 
void printArray(Student stu[],int n){
	cout<<"姓名\t语文\t数学\t英语\t总分\n"; 
	for(int i=0;i<n;i++){
		cout<<stu[i].name<<"\t"<<stu[i].yw<<"\t"<<stu[i].sx<<"\t"<<stu[i].yy<<"\t"<<stu[i].zf<<endl;
	} 
	cout<<endl<<endl;
}

int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>stu[i].name>>stu[i].yw>>stu[i].sx>>stu[i].yy;
		stu[i].zf = stu[i].yw + stu[i].sx + stu[i].yy;
	}
	
	//输出原始数据 
	printArray(stu,n);
	
	//按照总分降序排序 
	sort(stu,stu+n,cmp);
	printArray(stu,n);

	return 0;
} 

输入下面的测试数据,运行结果如下:

6
Rose 80 90 100
Jerry 89 90 91
Jack 78 89 97
Lily 88 89 90
Tom 66 68 87
Frank 88 90 89
姓名    语文    数学    英语    总分
Rose    80      90      100     270
Jerry   89      90      91      270
Jack    78      89      97      264
Lily    88      89      90      267
Tom     66      68      87      221
Frank   88      90      89      267


姓名    语文    数学    英语    总分
Rose    80      90      100     270
Jerry   89      90      91      270
Lily    88      89      90      267
Frank   88      90      89      267
Jack    78      89      97      264
Tom     66      68      87      221

可见出现了相同分数的同学,如果要进一步指定对于分数相同的同学,按照姓名的字典顺序从小到大排序(相当于Excel中排序时第一关键字是总分,排序方向是降序;第二关键字是姓名,排序方向是升序),其实只需要修改自定义排序函数即可:

//供sort函数使用的比较函数:实现灵活的排序 
//参数使用const Student&类型和Student类型均可,不过前者执行效率更高 
bool cmp(const Student& a,const Student& b){
	//总分相等,按照姓名字典顺序升序排序(一般做法是越特殊越优先处理,这里先处理第二关键字) 
	if(a.zf==b.zf) return a.name<b.name;
	//按照总分降序排序 
	return a.zf>b.zf;
}

比较函数的写法不唯一,上面的比较函数也可以写成下面这样,注意比较两者的区别:

//供sort函数使用的比较函数:实现灵活的排序 
//参数使用const Student&类型和Student类型均可,不过前者执行效率更高 
bool cmp(const Student& a,const Student& b){
	//总分不相等,按照总分降序排序
	if(a.zf!=b.zf) return a.zf>b.zf;
	//(运行到这里则总分相等)按照姓名字典顺序升序排序 
	return a.name<b.name;
}

更高级的用法是重载针对结构体变量的<运算符,这个时候可以简单调用sort函数直接对结构体数组排序:

#include<iostream>
#include<algorithm> 
using namespace std;

const int N = 50;		//最多50位学生
//将一位学生的多项信息“捆绑”成一个结构体 
struct Student{
	string name;	   //学生姓名
	int yw,sx,yy;		//考试成绩
	int zf;
};
//重载<运算符,实现Student变量的小于比较运算
//注意这里的<运算符的比较规则是:x.zf总分不低于y.zf,如果总分相等,那么x.name字典顺序不高于y.name ,此时认为x小于y(sort排序时出现在前面)
bool operator <(const Student &x,const Student &y){
	if(x.zf==y.zf) return x.name<y.name; 
    return x.zf>y.zf;
}
//Student结构体重载了<运算符,直接return b<a;
bool cmp(const Student &a,const Student &b){
	return b<a;
} 

Student stu[N+1]; 

//输出结构体数组信息 
void printArray(Student stu[],int n){
	cout<<"姓名\t语文\t数学\t英语\t总分\n"; 
	for(int i=0;i<n;i++){
		cout<<stu[i].name<<"\t"<<stu[i].yw<<"\t"<<stu[i].sx<<"\t"<<stu[i].yy<<"\t"<<stu[i].zf<<endl;
	} 
	cout<<endl<<endl;
}

int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>stu[i].name>>stu[i].yw>>stu[i].sx>>stu[i].yy;
		stu[i].zf = stu[i].yw + stu[i].sx + stu[i].yy;
	}

	//测试结构体<运算符 
	if(stu[0]<stu[1]) cout<<"stu[0]<stu[1] 成立"<<endl;
	else cout<<"stu[0]<stu[1] 不成立"<<endl;

	//输出原始数据 
	printArray(stu,n);
	
	//Student结构体重载了<运算符,那么可以简单调用sort函数
	sort(stu,stu+n);
	printArray(stu,n);

	//指定比较函数cmp
	sort(stu,stu+n,cmp);
	printArray(stu,n);

	return 0;
} 

除了像上面在结构体外部重载运算符,还可以在结构体内部重载运算符(了解即可,弄懂有一定难度):

#include<iostream>
#include<algorithm> 
using namespace std;

const int N = 50;        //最多50位学生
//将一位学生的多项信息“捆绑”成一个结构体 
struct Student{
    string name;       //学生姓名
    int yw,sx,yy;        //考试成绩
    int zf;
    
    //重载<运算符,实现Student变量的小于比较运算
    //注意这里的<运算符的比较规则是:总分不低于ot.zf,如果总分相等,那么姓名字典顺序不高于ot.name ,此时认为小于ot(sort排序时出现在前面)
    bool operator <(const Student &ot) const{
        if(zf==ot.zf) return name<ot.name; 
        return zf>ot.zf;
    } 
};
//Student结构体重载了<运算符,直接return b<a;
bool cmp(const Student &a,const Student &b){
    return b<a;
} 

Student stu[N+1]; 

//输出结构体数组信息 
void printArray(Student stu[],int n){
    cout<<"姓名\t语文\t数学\t英语\t总分\n"; 
    for(int i=0;i<n;i++){
        cout<<stu[i].name<<"\t"<<stu[i].yw<<"\t"<<stu[i].sx<<"\t"<<stu[i].yy<<"\t"<<stu[i].zf<<endl;
    } 
    cout<<endl<<endl;
}

int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>stu[i].name>>stu[i].yw>>stu[i].sx>>stu[i].yy;
        stu[i].zf = stu[i].yw + stu[i].sx + stu[i].yy;
    }

    //测试结构体<运算符 
    if(stu[0]<stu[1]) cout<<"stu[0]<stu[1] 成立"<<endl;
    else cout<<"stu[0]<stu[1] 不成立"<<endl;

    //输出原始数据 
    printArray(stu,n);
    
    //Student结构体重载了<运算符,那么可以简单调用sort函数
    sort(stu,stu+n);
    printArray(stu,n);

    //指定比较函数cmp
    sort(stu,stu+n,cmp);
    printArray(stu,n);

    return 0;
}