C++ 结构体与结构体数组详解:定义、排序与实战应用
导读: 在实际开发中,我们经常需要把多个不同类型的数据绑定在一起表示一个实体——比如一个学生的学号(int)、姓名(string)、成绩(double)。C++ 的
struct就是做这件事的。本文从结构体的定义讲起,逐步扩展到结构体数组、结构体排序,最后通过三道经典题目演示结构体在实战中的应用。
一、结构体是什么?
C++ 内置的类型(int、double、string)一次只能存一个值。但现实中的数据往往是"一组一组"出现的:一个学生有学号、姓名、年龄、成绩等多个属性,一个坐标有 x 和 y 两个值。
**结构体(struct)**就是把多个不同类型的变量打包成一个自定义类型:
// 定义一个"学生"结构体
struct student {
int num; // 学号
string name; // 姓名
int age; // 年龄
double score; // 成绩
};
定义完成后,student 就和 int、string 一样,是一个可用的类型了。
C vs C++ 小知识: C 语言中习惯用
typedef struct student {} student;来简化命名。C++ 中直接写struct student {}就行,typedef可以省略,使用时也不需要加struct关键字。
二、创建和使用结构体变量
2.1 用结构体类型创建变量
定义好结构体后,可以像内置类型一样创建变量:
student st; // 创建一个学生变量
st.num = 1001; // 用 "." 访问成员
st.name = "张三";
st.age = 20;
st.score = 95.5;
st.num 就像"张三的学号",. 运算符用来访问结构体内部的某个字段。
2.2 创建时直接初始化
也可以在创建的同时赋值:
student st = {1002, "李四", 21, 88.0};
字段按照定义顺序依次赋值,不想赋值的可以留空(默认初始化为 0 或空字符串)。
2.3 用 typedef 给结构体起别名
如果嫌 struct student 写起来太长,可以用 typedef 起个短名字:
typedef struct student {
int num;
string name;
int age;
double score;
} stu; // stu 就是 struct student 的别名
stu st1; // 和 student st1; 等价
stu st2;
三、结构体数组:把多个"对象"放在一起
一个 student 变量只能存一个学生的信息。如果要存全班 50 个学生呢?这就需要结构体数组。
3.1 普通数组形式
stu arr[50]; // 创建一个能存 50 个学生的数组
// 访问第 i 个学生的学号
arr[0].num = 1001;
arr[1].name = "王五";
和普通数组一样用 [] 取下标,然后 . 取字段。
3.2 vector 形式(推荐)
实际开发中更推荐用 vector,因为大小可以动态调整:
vector<stu> vec;
// 创建一个临时变量,填充数据后 push 进去
stu s;
s.num = 1001;
s.name = "张三";
s.score = 95.5;
vec.push_back(s);
// 也可以直接在 push_back 时用花括号构造(C++11)
vec.push_back({1002, "李四", 21, 88.0});
技巧: 如果数据量已知,可以直接创建指定大小的 vector:
vector<stu> vec(50); // 预分配 50 个位置,每个字段默认初始化 vec[1].num = 1001;
3.3 遍历结构体 vector
// 增强 for 循环遍历
for (auto &s : vec) {
cout << s.num << " " << s.name << " " << s.score << endl;
}
用引用 & 避免拷贝,提高效率。
四、结构体排序:sort + lambda 表达式
这是结构体最实用的考点。实际题目中,我们经常需要对学生按成绩排序、对坐标按 x 排序等等。关键在于:怎么告诉 sort 按结构体的哪个字段来排?
4.1 基本用法
用 lambda 表达式指定排序规则:
vector<stu> vec = {{1001, "张三", 20, 95.5},
{1002, "李四", 21, 88.0},
{1003, "王五", 19, 92.0}};
// 按成绩从高到低排序
sort(vec.begin(), vec.end(), [](const stu &a, const stu &b) {
return a.score > b.score; // a 的成绩 > b 的成绩时,a 排在前面
});
lambda 表达式的规则很简单:参数是两个待比较的元素 a 和 b,return true 表示 a 应该排在 b 前面。
4.2 多字段排序
实际题目经常要求"成绩相同则按学号排"这种多级排序:
// 先按成绩降序,成绩相同则按学号升序
sort(vec.begin(), vec.end(), [](const stu &a, const stu &b) {
if (a.score != b.score) return a.score > b.score;
return a.num < b.num;
});
逻辑是:先比第一个字段,如果相等再比第二个字段,以此类推。用 if 逐级判断即可。
五、实战题目
5.1 谁考了第 k 名
题目:输入 n 个学生的学号和成绩,按成绩从高到低排序后输出第 k 名的学号和成绩。
思路: 用结构体存学号和成绩,排序后直接取第 k-1 个元素。
#include<bits/stdc++.h>
using namespace std;
struct stu {
string num; // 学号
double score; // 成绩
};
vector<stu> vec;
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
stu s;
cin >> s.num >> s.score;
vec.push_back(s);
}
// 按成绩降序排序
sort(vec.begin(), vec.end(), [](const stu &a, const stu &b) {
return a.score > b.score;
});
// 第 k 名就是下标 k-1(下标从 0 开始)
cout << vec[k - 1].num << " " << vec[k - 1].score;
return 0;
}
关键点: 结构体只定义需要的字段,不要贪多。这道题只需要学号和成绩,就不用加 name 和 age。
5.2 奖学金
题目:输入 n 个学生的语文、数学、英语成绩,计算总分后按以下规则排序取前 5 名:
- 总分高的在前
- 总分相同,语文成绩高的在前
- 总分和语文都相同,学号小的在前
这是一道经典的三级排序题,排序规则稍微复杂但逻辑清晰。
思路: 结构体里存学号、各科成绩和总分,用 lambda 实现三级排序。
#include<bits/stdc++.h>
using namespace std;
struct sc {
int num; // 学号
int chin; // 语文
int math; // 数学
int eng; // 英语
int score; // 总分
};
vector<sc> vec;
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
sc s;
s.num = i;
cin >> s.chin >> s.math >> s.eng;
s.score = s.chin + s.math + s.eng; // 计算总分
vec.push_back(s);
}
// 三级排序:总分 -> 语文 -> 学号
sort(vec.begin(), vec.end(), [](const sc &a, const sc &b) {
if (a.score != b.score) return a.score > b.score;
if (a.chin != b.chin) return a.chin > b.chin;
return a.num < b.num;
});
// 输出前 5 名
for (int i = 0; i < 5; i++) {
cout << vec[i].num << " " << vec[i].score << endl;
}
return 0;
}
关键点: 多级排序时,每一级判断完 != 后再进入下一级,这样逻辑最清晰,不会出错。
5.3 合并区间(LeetCode 56)
题目:给定若干个区间
[start, end],将所有有重叠的区间合并。
虽然这道题用的是 vector<vector<int>> 而非自定义结构体,但它的核心思路和结构体排序完全一致——先按某个字段排序,再遍历处理。
思路: 先按区间左端点排序,然后遍历判断相邻区间是否重叠。重叠则合并(取更大的右端点),不重叠则直接加入结果。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.empty()) return intervals;
// 按区间左端点升序排序
sort(intervals.begin(), intervals.end(),
[](const vector<int> &a, const vector<int> &b) {
return a[0] < b[0];
});
vector<vector<int>> result;
result.push_back(intervals[0]); // 先把第一个区间放进去
for (int i = 1; i < intervals.size(); i++) {
// 当前区间的左端点 <= 结果中最后一个区间的右端点 → 重叠
if (intervals[i][0] <= result.back()[1]) {
// 合并:取更大的右端点
result.back()[1] = max(result.back()[1], intervals[i][1]);
} else {
// 不重叠:直接加入结果
result.push_back(intervals[i]);
}
}
return result;
}
};
关键点: result.back() 取的是结果中最后一个区间,用它来判断是否和当前区间重叠。这种"先排序再贪心合并"的思路在很多区间类题目中都适用。
六、总结
| 知识点 | 关键内容 |
|---|---|
| 结构体定义 | struct 名字 { 字段... };,不同类型数据打包 |
| 成员访问 | 用 . 运算符:st.name |
| 结构体数组 | 普通数组 stu arr[N] 或 vector<stu> |
| 结构体排序 | sort + lambda 表达式指定比较字段 |
| 多级排序 | if (字段1 != 字段1) return 比较; if (字段2 != 字段2) return 比较; |
| typedef | typedef struct xxx {} 别名;,简化类型名 |
结构体是 C++ 中连接"数据"和"逻辑"的桥梁。后续学习链表、二叉树等数据结构时,节点本质上就是结构体。把结构体用熟练,后面的内容会轻松很多。
更多推荐
所有评论(0)