Skip to content

📦 08. 数据结构

在编程里,我们经常需要存储和处理大量的数据。比如你想保存班上每个学生的成绩、姓名、年龄等信息,单靠一个个变量显然不现实,也不方便管理。

这时候,数据结构就派上用场了。数据结构是一种组织和管理数据的方法,它帮助我们把相关的数据组合起来,让程序更高效、更清晰。

为什么需要数据结构?

  • 高效管理大量数据:用一个数组就能存储多个学生成绩,避免写一堆单独变量。
  • 方便操作和访问:通过下标可以快速找到想要的数据。
  • 增强程序可读性和维护性:用结构把学生的姓名、年龄、成绩等信息打包成一个整体,让代码更有逻辑,更容易理解和修改。
  • 为解决复杂问题打基础:后续学到的排序、查找、算法等,都依赖于良好的数据组织。

数组和结构的用处

  • 数组:适合存储相同类型的多个数据,比如一组整数、多个字符、连续的分数等。它让我们能用同一个名字管理多个数据,方便循环和批量处理。
  • 结构:适合存储不同类型的数据,比如一个学生的姓名(字符串)、年龄(整数)、成绩(浮点数)等,把这些相关信息组合成一个整体,让数据管理更直观。

1. 数组

1.1 一维数组

数组是一组相同类型数据的连续集合,用来存储多个相同类型的变量。

cpp
int nums[5];  // 定义一个可以存储5个整数的数组nums
float scores[6];// 定义一个可以存储5个浮点数的数组scores

元素指的是数组内的数据,比如int nums[5]中,分别有五个int类型的变量,它们就称为数组的元素。

1.1.1 一维数组的初始化

cpp
int nums[3] = {1, 2, 3}; //定义一个 nums 数组,并初始化其3个元素为 1,2,3
char letters[4] = {'a', 'b', 'c', '\0'};

这里初始化的意思指的是在定义一个数组的时候给其赋值,需要特别注意,只有在初始化的时候可以使用这种整体赋值的方式,在后续的使用过程中,如果需要赋值,则需要逐个赋值。

1.1.2 访问和修改数组元素

cpp
cout << nums[0];  // 访问第一个元素并打印出来
nums[1] = 10;      // 修改第二个元素为10

我们可以通过数组下标索引)来访问数组的元素,需要注意,数组的下标是从0开始的,所以第一个元素是 nums[0]

1.1.3 遍历数组

cpp
for (int i = 0; i < 3; i++) {
    cout << nums[i] << endl;
}

TIP

数组下标从0开始,最后一个元素的下标是 n-1。

1.2 二维数组

二维数组可以看作“数组的数组”,用于存储表格状的数据。

cpp
int matrix[2][3];  // 2行3列的二维数组

matrix是一个二维数组,它是一个包含两个三个元素数组的一个数组,也就是它的第一个元素和第二个元素都是一个数组,而且这个数组拥有3个元素。所以这个二维数组就可以存放2*3=6int变量。

1.2.1 二维数组的初始化

cpp
int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

1.2.2 访问二维数组元素

cpp
cout << matrix[0][1];  // 输出第一行第二列的元素,结果是2
matrix[1][2] = 10;     // 修改第二行第三列的元素为10

1.2.3 遍历二维数组

cpp
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        cout << matrix[i][j] << " ";
    }
    cout << endl;
}

遍历二维数组需要用到嵌套的for循环,这在初期阶段可能比较难以理解,可以暂时不掌握。

1.3 字符数组

字符数组用于存储字符串,注意必须以 \0 结束。

cpp
char name[10];
cin >> name;  // 输入字符串,自动添加\0
char word[] = {'H', 'i', '\0'};  // 手动赋值

TIP

'\0` 表示数字0而非字符0,它表示一个字符串到此结束了,之所以需要它,是因为内存每一个空间都是有值的,计算机在输出的时候,并不知道这个字符串到哪里结束了,所以应该约定一个用来表示结束的符号。

1.3.1 字符串函数

由于我们无法直接整体的给字符串赋值,除非在定义的时候,所有后续我们如果要复制需要单独每个元素赋值,这有一些麻烦。cstring提供了给字符串赋值的函数,方便我们一次性赋值:

cpp
#include <cstring>          //需要包含 <cstring> 库文件

strlen(name);               // 计算字符串长度(不包括\0)
strcpy(name, "Xianzi");     // 复制字符串

NOTE

C风格字符串必须以 \0 结尾,否则会导致程序错误。C风格的字符串就是字符数组,C++有新的字符串类型。

  • 字符数组错误的写法
cpp
char y[4] = {'2', '3', '2', '0'};  // 没有 \0 结尾
printf("y的值为:%s\n", y);        // 输出时会异常
  • 正确的写法:
cpp
char y[5] = {'2', '3', '2', '0', '\0'};
printf("y的值为:%s\n", y);        // 正常输出

2. 结构

2.1 什么是结构

结构(struct)是自定义的数据类型,能把不同类型的数据组合成一个整体。

cpp
struct Student {
    char name[20];
    int age;
};

假设我们需要一个学生管理的系统,这样我们就可以通过结构把一个学生的数据全都存储进去,比如名字,年龄,月考的分数等等,这样就非常方便我们管理数据,比如可以直接查找某个结构里面分数为95的学生。

2.2 定义结构变量并使用

当我们定义好一个结构后,就可以使用它来定义一个结构变量:

cpp
//定义一个 Student 结构
//它包含了一个可以存放20个字符的 name 字符数组,和
//一个存放年龄的 int 变量
struct Student {
    char name[20];
    int age;
};

Student s1;   //定义一个结构

像这样我们就定义了一个结构,接着就是进行访问赋值。由于结构有很多成员,所以我们使用.来访问:

cpp
Student s1;   //定义一个结构 s1
strcpy(s1.name, "小明"); //赋值,不能使用 s1.name = "小明 " 
s1.age = 12; // int 可以直接赋值

2.2.1 结构的初始化

和数组一样,结构也只有在定义的时候可以用 {} 整体初始化,按成员顺序依次赋值:

cpp
Student s2 = {"小红", 11};    // name="小红", age=11
Student s3 = {"小刚", 10};    // name="小刚", age=10

顺序要和结构定义里的成员顺序一致——先 nameage,不能颠倒。如果只想初始化部分成员,后面的可以不写,它们会被设为默认值(0 或空字符串):

cpp
Student s4 = {"小蓝"};  // name="小蓝", age=0(默认)

TIP

和数组一样,只有定义的时候可以用 {} 整体赋值。后面如果再想改值,只能一个个成员单独赋值,比如 s2.age = 12

2.3 结构数组

cpp
Student classA[30];  // 一个班级的学生数组

一个班级通常有很多个学生,所以我们可以将结构作为数组元素,存放在数组中。这个结构数组 classA 拥有30个 Student 结构。

3. 例题

学完上面的知识点,我们来看几个综合例子,看看数组和结构在实际中怎么用。

3.1 数组例1 — 逆序输出

输入 5 个整数存入数组,然后按相反的顺序输出。

cpp
#include <iostream>
using namespace std;

int main() {
    int nums[5];

    // 输入5个数
    cout << "请输入5个整数:";
    for (int i = 0; i < 5; i++) {
        cin >> nums[i];
    }

    // 逆序输出:从最后一个下标开始往前遍历
    cout << "逆序输出:";
    for (int i = 4; i >= 0; i--) {
        cout << nums[i] << " ";
    }
    cout << endl;

    return 0;
}

TIP

数组逆序的要点是下标从 n-1 开始,一直减到 0。这个思路在后续的排序、翻转等算法中很常见。


3.2 数组例2 — 统计分数段

输入 10 个学生的成绩(0-100 分),统计优秀(≥90)、及格(60-89)、不及格(<60)各有多少人。

cpp
#include <iostream>
using namespace std;

int main() {
    int scores[10];
    int excellent = 0, pass = 0, fail = 0;

    cout << "请输入10个成绩:";
    for (int i = 0; i < 10; i++) {
        cin >> scores[i];
    }

    // 遍历数组,按分数段统计
    for (int i = 0; i < 10; i++) {
        if (scores[i] >= 90) {
            excellent++;
        } else if (scores[i] >= 60) {
            pass++;
        } else {
            fail++;
        }
    }

    cout << "优秀:" << excellent << "人" << endl;
    cout << "及格:" << pass << "人" << endl;
    cout << "不及格:" << fail << "人" << endl;

    return 0;
}

这种用循环遍历数组、分条件统计的方法,在实际项目里经常用到。


3.3 结构例1 — 找最高分学生

定义一个学生结构,包含姓名和成绩。输入 3 个学生的信息,找出成绩最高的那个并输出。

cpp
#include <iostream>
using namespace std;

struct Student {
    char name[20];
    int score;
};

int main() {
    Student students[3];
    int maxIdx = 0;  // 记录最高分学生的下标

    // 输入3个学生的信息
    for (int i = 0; i < 3; i++) {
        cout << "请输入第" << i + 1 << "个学生的姓名和成绩:";
        cin >> students[i].name >> students[i].score;
    }

    // 找出成绩最高的下标
    for (int i = 1; i < 3; i++) {
        if (students[i].score > students[maxIdx].score) {
            maxIdx = i;
        }
    }

    cout << "最高分: " << students[maxIdx].name
         << " ," << students[maxIdx].score << "分" << endl;

    return 0;
}

TIP

这种"先假设第一个是最大值,然后逐个比较更新"的思路叫打擂台法,在找最大值、最小值时非常实用。


3.4 结构例2 — 计算两点距离

定义一个点结构 Point,包含 x 和 y 坐标。输入两个点的坐标,计算它们之间的距离。

cpp
#include <iostream>
#include <cmath>  // 用 sqrt 开平方
using namespace std;

struct Point {
    int x;
    int y;
};

int main() {
    Point p1, p2;

    cout << "请输入第一个点的坐标 (x y):";
    cin >> p1.x >> p1.y;
    cout << "请输入第二个点的坐标 (x y):";
    cin >> p2.x >> p2.y;

    // 计算两点之间的距离:√[(x1-x2)² + (y1-y2)²]
    int dx = p1.x - p2.x;
    int dy = p1.y - p2.y;
    double dist = sqrt(dx * dx + dy * dy);

    cout << "两点之间的距离为:" << dist << endl;

    return 0;
}

NOTE

两点间距离公式来自勾股定理。这里 sqrt()cmath 库提供的开平方函数,使用前记得 #include <cmath>


3.5 结构数组例1 — 全班成绩统计

结合结构和数组,输入 5 个学生的姓名和成绩,计算平均分,并输出所有学生的信息。

cpp
#include <iostream>
using namespace std;

struct Student {
    char name[20];
    int score;
};

int main() {
    Student classA[5];
    int sum = 0;

    // 输入5个学生信息
    for (int i = 0; i < 5; i++) {
        cout << "请输入第" << i + 1 << "位学生的姓名和成绩:";
        cin >> classA[i].name >> classA[i].score;
        sum += classA[i].score;  // 累加总分
    }

    double avg = (double)sum / 5;
    cout << "\n===== 班级成绩单 =====" << endl;
    cout << "平均分:" << avg << endl;
    cout << "---------------------" << endl;

    // 输出每个学生的信息
    for (int i = 0; i < 5; i++) {
        cout << classA[i].name << ":" << classA[i].score << "分";
        // 标注是否高于平均分
        if (classA[i].score >= avg) {
            cout << " ✅ 高于平均分";
        }
        cout << endl;
    }

    return 0;
}

TIP

结构数组把两个知识点的威力结合起来了 — 数组管理大量数据,结构管理复杂信息。学生管理系统、图书管理系统等实际项目,都是从这种结构数组开始的。


4. 小结

数组让我们可以把多个相同类型的数据放在一起管理,通过下标快速访问。一维数组就像一排带编号的柜子,二维数组就像一张表格,字符数组用来存字符串。记住下标从 0 开始,字符数组要以 \0 结尾。

结构让我们可以把不同类型的数据打包成一个整体,比如一个学生既有名字(字符数组)又有年龄(整数)又有成绩(浮点数)。定义好结构后,用 . 来访问里面的成员。

结构数组就是把结构和数组结合起来 — 用数组管理多个结构,比如一个班 30 个学生。这在写真正的程序时非常常用。

本节课知识点回顾:

概念要点
一维数组int arr[5],下标从 0 到 n-1
二维数组int mat[3][4],像表格,先遍历行再遍历列
字符数组\0 结尾,可用 strlen/strcpy 操作
结构struct 自定义类型,用 . 访问成员
结构数组Student classA[30],数组的每个元素都是结构

5. 练习题

数组练习

  1. 定义一个整型数组 scores,输入5个学生成绩,输出所有成绩。
  2. 输入一个包含10个数字的数组,找最大值和最小值并输出。
  3. 输入10个整数,计算它们的平均值并输出(结果保留一位小数)。
  4. 创建整型数组 data[5],偶数位置赋1,奇数位置赋0,输出数组。
  5. 输入8个整数,统计其中正数、负数、零的个数。
  6. 输入6个数,将它们逆序存入另一个数组并输出。
  7. 输入10个成绩,统计≥80分的有多少人。
  8. 定义字符数组 name,用 cin 输入你的名字,输出字符串长度。
  9. 定义字符数组 word[20],手动赋值为 "Hello",循环输出每个字符。
  10. 输入一个字符串,统计其中大写字母、小写字母、数字的个数。
  11. 输入一个英文单词,判断是否是回文字符串(正读反读相同)。

结构练习

  1. 创建结构 Student 包含姓名和年龄,定义两个学生并输出信息。
  2. 定义结构 Point,包含坐标 xy,输入一个点,输出坐标。
  3. 定义结构 Rect 表示矩形,包含长和宽,计算并输出面积。
  4. 定义结构 Circle,包含圆心 Point 和半径 r,输入信息并输出面积。
  5. 定义结构 Book,包含书名、作者、价格,输入3本书的信息,输出最贵的那本。
  6. 输入一个日期(年、月、日),用结构存储,判断该日期是否合法(注意闰年2月)。
  7. 定义结构 Student 包含姓名、语文成绩、数学成绩,输入一个学生,输出总分。

结构数组练习

  1. 定义 Student 结构(姓名+成绩),输入5个学生,输出所有高于平均分的学生。
  2. 输入10个学生的姓名和成绩,找出最高分和最低分的学生并输出。
  3. 定义 Point 结构,输入3个点的坐标,计算并输出距离原点最近的点。
  4. 输入一个班30个学生的成绩,统计各分数段的人数(优秀/及格/不及格)。
  5. 定义结构 Student(姓名+3科成绩),输入5个学生,输出每个学生的总分和平均分。
  6. 输入5本书的信息(书名+价格),按价格从高到低排序后输出。(提示:用冒泡排序)⭐
  7. 定义 Student 结构(姓名+成绩),输入10个学生,实现按成绩排名输出。(不改变原数组顺序)⭐⭐