导读: 在实际开发中,我们经常需要把多个不同类型的数据绑定在一起表示一个实体——比如一个学生的学号(int)、姓名(string)、成绩(double)。C++ 的 struct 就是做这件事的。本文从结构体的定义讲起,逐步扩展到结构体数组、结构体排序,最后通过三道经典题目演示结构体在实战中的应用。


一、结构体是什么?

C++ 内置的类型(intdoublestring)一次只能存一个值。但现实中的数据往往是"一组一组"出现的:一个学生有学号、姓名、年龄、成绩等多个属性,一个坐标有 x 和 y 两个值。

**结构体(struct)**就是把多个不同类型的变量打包成一个自定义类型:

// 定义一个"学生"结构体
struct student {
    int num;        // 学号
    string name;    // 姓名
    int age;        // 年龄
    double score;   // 成绩
};

定义完成后,student 就和 intstring 一样,是一个可用的类型了。

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 表达式的规则很简单:参数是两个待比较的元素 abreturn 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 名:

  1. 总分高的在前
  2. 总分相同,语文成绩高的在前
  3. 总分和语文都相同,学号小的在前

这是一道经典的三级排序题,排序规则稍微复杂但逻辑清晰。

思路: 结构体里存学号、各科成绩和总分,用 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++ 中连接"数据"和"逻辑"的桥梁。后续学习链表、二叉树等数据结构时,节点本质上就是结构体。把结构体用熟练,后面的内容会轻松很多。

更多推荐