Appearance
📦 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=6个int变量。
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; // 修改第二行第三列的元素为101.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顺序要和结构定义里的成员顺序一致——先 name 再 age,不能颠倒。如果只想初始化部分成员,后面的可以不写,它们会被设为默认值(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. 练习题
数组练习
- 定义一个整型数组
scores,输入5个学生成绩,输出所有成绩。 - 输入一个包含10个数字的数组,找最大值和最小值并输出。
- 输入10个整数,计算它们的平均值并输出(结果保留一位小数)。
- 创建整型数组
data[5],偶数位置赋1,奇数位置赋0,输出数组。 - 输入8个整数,统计其中正数、负数、零的个数。
- 输入6个数,将它们逆序存入另一个数组并输出。
- 输入10个成绩,统计≥80分的有多少人。
- 定义字符数组
name,用cin输入你的名字,输出字符串长度。 - 定义字符数组
word[20],手动赋值为 "Hello",循环输出每个字符。 - 输入一个字符串,统计其中大写字母、小写字母、数字的个数。
- 输入一个英文单词,判断是否是回文字符串(正读反读相同)。
结构练习
- 创建结构
Student包含姓名和年龄,定义两个学生并输出信息。 - 定义结构
Point,包含坐标x和y,输入一个点,输出坐标。 - 定义结构
Rect表示矩形,包含长和宽,计算并输出面积。 - 定义结构
Circle,包含圆心Point和半径r,输入信息并输出面积。 - 定义结构
Book,包含书名、作者、价格,输入3本书的信息,输出最贵的那本。 - 输入一个日期(年、月、日),用结构存储,判断该日期是否合法(注意闰年2月)。
- 定义结构
Student包含姓名、语文成绩、数学成绩,输入一个学生,输出总分。
结构数组练习
- 定义
Student结构(姓名+成绩),输入5个学生,输出所有高于平均分的学生。 - 输入10个学生的姓名和成绩,找出最高分和最低分的学生并输出。
- 定义
Point结构,输入3个点的坐标,计算并输出距离原点最近的点。 - 输入一个班30个学生的成绩,统计各分数段的人数(优秀/及格/不及格)。
- 定义结构
Student(姓名+3科成绩),输入5个学生,输出每个学生的总分和平均分。 - 输入5本书的信息(书名+价格),按价格从高到低排序后输出。(提示:用冒泡排序)⭐
- 定义
Student结构(姓名+成绩),输入10个学生,实现按成绩排名输出。(不改变原数组顺序)⭐⭐