第五章:批量存储数据——数组¶
你好!欢迎来到第五章!在前面的章节,我们学会了用变量存储单个数据,比如一个年龄、一个成绩。但如果要存储全班50个人的成绩,难道要定义50个变量吗?那太麻烦了!这时候就需要数组——它可以一次性定义多个相同类型的变量,就像一排带编号的柜子,每个柜子里可以放一个数据。这一章我们就来学习如何使用数组。
5.1 一维数组¶
5.1.1 数组程序范例¶
先看一个简单的例子:定义一个能装5个整数的数组,然后给它们赋值并输出。
#include <iostream>
using namespace std;
int main() {
int a[5]; // 定义一个数组,名字叫a,里面可以放5个整数
// 给数组的每个元素赋值
a[0] = 10; // 第一个格子(下标0)放10
a[1] = 20; // 第二个格子(下标1)放20
a[2] = 30;
a[3] = 40;
a[4] = 50;
// 输出数组的每个元素
cout << a[0] << " " << a[1] << " " << a[2] << " " << a[3] << " " << a[4] << endl;
return 0;
}
运行结果:
10 20 30 40 50
5.1.2 数组的用法¶
什么是数组?¶
数组就是一组相同类型的数据的集合,它们在内存中连续存放。每个数据叫做数组元素,通过下标(也叫索引)来访问。下标从0开始编号。
-
定义格式:
数据类型 数组名[元素个数];
例如:int score[5];定义了一个名为score的数组,可以存5个整数。 -
访问元素:
数组名[下标],下标范围是0到元素个数-1。
例如:score[0] = 98;给第一个元素赋值。
数组的初始化¶
可以在定义时直接赋值:
int a[5] = {10, 20, 30, 40, 50}; // 全部初始化
int b[] = {1, 2, 3, 4, 5}; // 不写个数,编译器自动计算为5
int c[5] = {1, 2}; // 只给前两个赋值,后面3个默认为0
使用循环遍历数组¶
数组通常和循环一起使用,因为可以用循环变量作为下标。
int a[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
cout << a[i] << " "; // 依次输出每个元素
}
数组的注意事项¶
- 下标不能越界,比如定义
int a[5];,只能使用a[0]到a[4],使用a[5]会导致未定义行为(可能程序崩溃)。 - 数组一旦定义,大小不能改变。
5.1.3 编程实例讲解¶
实例1:从键盘输入5个整数,求它们的和与平均值¶
#include <iostream>
using namespace std;
int main() {
int a[5];
int sum = 0;
cout << "请输入5个整数:";
for (int i = 0; i < 5; i++) {
cin >> a[i]; // 输入存到数组
sum += a[i]; // 累加
}
double avg = (double)sum / 5; // 平均值
cout << "和:" << sum << ",平均值:" << avg << endl;
return 0;
}
实例2:找出数组中的最大值和最小值¶
#include <iostream>
using namespace std;
int main() {
int a[5] = {34, 67, 12, 89, 45};
int max = a[0]; // 假设第一个是最大值
int min = a[0]; // 假设第一个是最小值
for (int i = 1; i < 5; i++) { // 从第二个开始比较
if (a[i] > max) {
max = a[i];
}
if (a[i] < min) {
min = a[i];
}
}
cout << "最大值:" << max << endl;
cout << "最小值:" << min << endl;
return 0;
}
实例3:数组元素逆序(把数组反过来)¶
#include <iostream>
using namespace std;
int main() {
int a[5] = {1, 2, 3, 4, 5};
int temp;
// 交换对称位置的元素
for (int i = 0; i < 5 / 2; i++) {
temp = a[i];
a[i] = a[4 - i]; // 4是最后一个元素的下标
a[4 - i] = temp;
}
cout << "逆序后的数组:";
for (int i = 0; i < 5; i++) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
运行结果:5 4 3 2 1
5.1.4 阶段性编程练习¶
- 练习1:定义一个包含10个整数的数组,用循环给数组赋值为1到10,然后输出。
- 练习2:输入8个整数,存入数组,然后输出所有大于平均数的数。
- 练习3:输入10个整数,查找某个数(由用户输入)是否在数组中,如果在,输出它的位置(下标),否则输出“未找到”。
- 练习4:输入一个正整数n(≤20),再输入n个整数,将它们从小到大输出(可以先不管排序,直接输出,排序下一节学)。但这里可以先练习用两个循环选择最小输出,或者简单用冒泡排序提前体验。
5.2 数组排序¶
排序就是把一组数按从小到大(升序)或从大到小(降序)排列。我们这里学习最简单的冒泡排序。
5.2.1 排序程序范例¶
#include <iostream>
using namespace std;
int main() {
int a[5] = {34, 67, 12, 89, 45};
int n = 5;
// 冒泡排序
for (int i = 0; i < n - 1; i++) { // 外层循环控制比较的轮数
for (int j = 0; j < n - 1 - i; j++) { // 内层循环控制每轮比较的次数
if (a[j] > a[j + 1]) { // 如果前一个比后一个大,交换
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
cout << "排序后的数组:";
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
运行结果:12 34 45 67 89
5.2.2 数组排序的用法¶
冒泡排序的原理¶
想象有一排泡泡,轻的往上浮,重的往下沉。冒泡排序就是每次比较相邻的两个数,如果顺序不对(比如前大后小,我们要升序),就交换它们。这样每一轮都会把最大的数“沉”到最后。
- 第一轮:从第一个元素开始,依次比较相邻的两个,如果前>后,交换。第一轮结束后,最大的数就到了最后。
- 第二轮:再从第一个开始,比较到倒数第二个(因为最后一个已经最大了),把第二大的数放到倒数第二。
- 重复n-1轮,所有数就排好了。
代码解释¶
- 外层循环
i控制轮数,一共需要 n-1 轮(因为最后剩下一个不用排)。 - 内层循环
j控制比较范围,每一轮比较的范围逐渐缩小,因为末尾已经排好的元素不用再比。所以j从0到 n-1-i。 - 如果
a[j] > a[j+1],交换。
其他排序方法(拓展了解)¶
- 选择排序:每一轮找到最小(或最大)的元素,放到前面。
- 插入排序:像打扑克牌,每次把一张牌插入到已排好序的手牌中。
但初学者先掌握冒泡排序就好。
5.2.3 编程实例讲解¶
实例4:输入n个数,升序输出¶
#include <iostream>
using namespace std;
int main() {
int n;
cout << "请输入数字个数:";
cin >> n;
int a[100]; // 假设最多100个数
cout << "请输入" << n << "个整数:";
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 冒泡排序
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
cout << "排序后:";
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
实例5:降序排序(从大到小)¶
只需要把比较条件 a[j] > a[j+1] 改成 a[j] < a[j+1] 即可。
if (a[j] < a[j + 1]) { // 如果前一个小于后一个,交换,让大的往前移
// 交换
}
实例6:对字符串数组排序(了解)¶
数组也可以是字符串类型,比如 string names[5]; 排序方法和整数类似,直接用 >、< 比较字符串(按字典序)。
5.2.4 阶段性编程练习¶
- 练习1:输入10个整数,用冒泡排序将它们从大到小输出。
- 练习2:输入n个学生的成绩,排序后输出,并输出最高分和最低分。
- 练习3:用选择排序实现升序排列(自学选择排序算法并实现)。
- 练习4:输入n个整数,去掉重复的数字后输出(提示:可以先排序,然后输出时跳过相邻相同的)。
5.3 二维数组¶
5.3.1 二维数组程序范例¶
二维数组就像一张表格,有行和列。比如定义一个3行4列的二维数组,并输出:
#include <iostream>
using namespace std;
int main() {
int a[3][4] = { // 3行4列
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 用双重循环输出每个元素
for (int i = 0; i < 3; i++) { // i控制行
for (int j = 0; j < 4; j++) { // j控制列
cout << a[i][j] << "\t"; // \t 是制表符,对齐
}
cout << endl; // 每行结束换行
}
return 0;
}
运行结果:
1 2 3 4
5 6 7 8
9 10 11 12
5.3.2 二维数组的用法¶
定义二维数组¶
格式:数据类型 数组名[行数][列数];
例如:int score[5][3]; 表示5行3列,可以存放5个学生3门课的成绩。
初始化¶
- 按行初始化:
cpp int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; - 连续赋值(会自动按行填):
cpp int a[2][3] = {1, 2, 3, 4, 5, 6}; // 等价于上面 - 部分初始化:未指定的元素默认为0。
访问元素¶
使用两个下标:数组名[行下标][列下标],行和列都从0开始。
例如:a[1][2] 表示第2行第3列的元素(在数学中常称为第2行第3列,但在编程中下标从0开始,所以实际是第2行第3个)。
遍历二维数组¶
几乎总是用嵌套循环:外层循环遍历行,内层循环遍历列。
5.3.3 编程实例讲解¶
实例7:输入一个3×4矩阵,求所有元素的和¶
#include <iostream>
using namespace std;
int main() {
int a[3][4];
int sum = 0;
cout << "请输入3行4列的矩阵:" << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cin >> a[i][j];
sum += a[i][j];
}
}
cout << "所有元素的和:" << sum << endl;
return 0;
}
实例8:求矩阵中的最大值及其位置¶
#include <iostream>
using namespace std;
int main() {
int a[3][4] = {
{34, 56, 12, 89},
{23, 45, 67, 90},
{11, 22, 33, 44}
};
int max = a[0][0];
int max_i = 0, max_j = 0; // 记录最大值的位置
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (a[i][j] > max) {
max = a[i][j];
max_i = i;
max_j = j;
}
}
}
cout << "最大值:" << max << ",位于第" << max_i+1 << "行第" << max_j+1 << "列" << endl;
return 0;
}
实例9:矩阵转置(行列互换)¶
假设有一个3行2列的矩阵,转置后变成2行3列。
#include <iostream>
using namespace std;
int main() {
int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
int b[2][3]; // 转置后的矩阵
// 转置:b[j][i] = a[i][j]
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
b[j][i] = a[i][j];
}
}
// 输出转置后的矩阵
cout << "转置后的矩阵:" << endl;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
cout << b[i][j] << " ";
}
cout << endl;
}
return 0;
}
运行结果:
转置后的矩阵:
1 3 5
2 4 6
5.3.4 阶段性编程练习¶
- 练习1:定义一个4×4的二维数组,用循环赋值为1到16,然后按矩阵形式输出。
- 练习2:输入一个3×3矩阵,计算主对角线(从左上到右下)上元素的和。
- 练习3:输入两个2×3矩阵,求它们的和(对应位置相加),输出结果矩阵。
- 练习4:输入一个5×5矩阵,判断它是否关于主对角线对称(即转置后等于原矩阵)。
5.4 第5章编程作业¶
恭喜你学完了数组!现在来挑战几个综合题目,综合运用一维、二维数组和排序。
作业1:成绩统计系统¶
输入一个班的学生人数n(≤30),再输入每个学生的姓名和5门课的成绩。要求:
- 计算每个学生的总分和平均分
- 按总分从高到低排序,并输出排名、姓名、总分、平均分
- 输出每门课的平均分(全班平均)
提示:可以用多个一维数组,或者结构体(但还没学结构体,可以用平行数组:名字数组、总分数组等,排序时同时交换名字和总分)。或者先简单做,只处理成绩。
作业2:矩阵乘法¶
输入两个矩阵,第一个是m×n,第二个是n×p,计算它们的乘积并输出。m,n,p都不超过10。
作业3:约瑟夫问题¶
有n个人围成一圈,从第1个人开始报数,数到m的人出列,然后从下一个人继续报数,直到所有人都出列。输出出列顺序。n和m由用户输入。(提示:可以用数组标记是否出列,用循环模拟)
作业4:统计单词¶
输入一行英文句子(包含空格),统计其中有多少个单词,并输出每个单词的长度。例如输入 “I love C++ programming”,输出单词个数4,以及每个单词的长度(1 4 3 11)。提示:可以用字符串数组,但还没学,可以用字符数组,用循环判断空格。
作业5:杨辉三角(二维数组版)¶
用二维数组存储杨辉三角的前n行(n由用户输入),然后输出。杨辉三角每个数等于它上方两数之和。