C++二维数组经典应用
较难19二维数组的经典应用:从成绩统计到图像处理
前面我们学会了如何定义和遍历一个二维数组,就像有了一个能存表格的神奇工具。那这个工具能在生活中派上什么用场呢?其实到处都是——班级成绩表、游戏地图、照片的像素、超市的货架……只要数据有行有列,就能用二维数组装进来。这一篇我们就来看两个最常用的场景,顺便学一些实战技巧。
应用1:学生成绩统计——按行求平均分
假设老师把3个学生、每人4门课的成绩记在一张表里,就像这样:
| 学生\科目 | 语文 | 数学 | 英语 | 科学 |
|---|---|---|---|---|
| 学生1 | 85 | 92 | 78 | 90 |
| 学生2 | 88 | 76 | 95 | 82 |
| 学生3 | 70 | 85 | 90 | 88 |
这张表在C++里就是一个二维数组,行是学生(3行),列是科目(4列)。我们用 int score[3][4]; 来存放。现在老师想算每个学生的平均分,也就是把每一行加起来除以4。
原来的代码已经写得很清楚了,我们补充两点:
- 变量
sum要每行重新清零,所以放在外层循环里面定义。 - 平均分用
sum / 4.0而不是sum / 4,因为整数除法会丢掉小数。
还可以顺便算一下全班的总平均分,这时需要把所有学生的所有分数加起来,再除以总人数×科目数。
下面是完整代码,每一行变量定义都加了中文注释:
#include <iostream>
using namespace std;
int main() {
// 3个学生、4门课的成绩,行=学生,列=科目
int score[3][4] = {
{85, 92, 78, 90}, // 学生1的成绩
{88, 76, 95, 82}, // 学生2的成绩
{70, 85, 90, 88} // 学生3的成绩
};
double class_sum = 0; // 全班总分数
for (int i = 0; i < 3; i++) { // i表示学生编号(从0开始)
int sum = 0; // 当前学生的总分,每行新开始
for (int j = 0; j < 4; j++) { // j表示科目编号
sum += score[i][j]; // 累加第i个学生的第j科成绩
}
double avg = sum / 4.0; // 4.0让结果变成小数
cout << "第" << i+1 << "个学生的平均分是:" << avg << endl;
class_sum += sum; // 把该生总分加到全班总分里
}
double class_avg = class_sum / (3 * 4); // 全班平均分
cout << "全班平均分是:" << class_avg << endl;
return 0;
}
小变体:如果要计算每门科的平均分(按列求和),就把外层循环换成 j(科目),内层循环换成 i(学生)。代码逻辑完全对称,只是把行和列角色互换。
常见错误提醒:
- 求和时忘记把
sum放在外层循环里面,导致第二个学生的总分包含了第一个学生的成绩。 - 用
sum / 4而不是sum / 4.0,结果被截断成整数,比如 85+92+78+90=345,345/4=86(正确应该是86.25)。 - 数组下标写反了,比如
score[j][i]导致访问到错误位置,可能越界。
应用2:图像中的像素矩阵——黑白格子也能画画
你看到的黑白照片,在计算机里其实就是一个二维数组!每个格子叫“像素”,里面放一个数字代表亮度:0是最黑(纯黑),255是最白(纯白),中间的数字就是不同深浅的灰色。比如一个4×4的简单图像,我们可以直接把它存成一个二维数组,然后打印出来看看像什么。
原来的例子已经打印了一个棋盘格子图案,我们再来丰富一下:假如你想画一个字母“X”,可以用二维数组表示成图样。下面这个程序先定义了一个5×5的“X”图案(黑=0,白=255),然后打印出来,同时算出图片里白色像素的个数(即亮度值等于255的点数)。
#include <iostream>
using namespace std;
int main() {
// 5行5列的图案,0表示黑,255表示白
int picture[5][5] = {
{255, 0, 0, 0, 255},
{ 0, 255, 0, 255, 0},
{ 0, 0, 255, 0, 0},
{ 0, 255, 0, 255, 0},
{255, 0, 0, 0, 255}
};
int white_count = 0; // 统计白色像素个数
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
if (picture[row][col] == 255) {
cout << "*"; // 白色用星号表示
white_count++;
} else {
cout << " "; // 黑色用空格表示
}
}
cout << endl; // 每行结束换行
}
cout << "白色像素共有 " << white_count << " 个" << endl;
return 0;
}
运行后你会看到屏幕中间一个白色的“X”形状。这种用二维数组表示图片的方法,就是最基础的图像处理——灰度图。现实中的照片有几百甚至几千万个像素,但原理完全一样。
常见错误提醒:
- 打印时忘记用
cout << endl;换行,结果所有像素挤在一行里,看不出图案。 - 把256(颜色范围是0~255)错当成255使用,或者把0和255理解反了。
- 给数组赋值时少写了花括号或者多写了逗号,导致编译错误。
更多玩法:矩阵运算与查找
二维数组还能做很多事,比如:
- 矩阵加法:两个同样大小的数组,对应位置相加。
- 矩阵转置:把行和列互换。
- 查找最大值:遍历整个数组,记录最大值及其位置。
- “扫雷”游戏:用二维数组存放地雷,每个格子统计周围8个格子的雷数。
下面给一个综合示例:输入一个3行3列的矩阵(比如游戏中的3×3棋盘),然后输出它的转置(行变列、列变行)。
#include <iostream>
using namespace std;
int main() {
// 定义3行3列的原始矩阵
int a[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int b[3][3]; // 用来存放转置后的结果
// 计算转置:b[j][i] = a[i][j]
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
b[j][i] = a[i][j];
}
}
// 输出转置后的矩阵
cout << "转置后的矩阵:" << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << b[i][j] << " ";
}
cout << endl;
}
return 0;
}
总结与下一步
二维数组就是把一维数组的每个位置再变成数组,就像一个表格。掌握它之后,你可以:
- 处理成绩表、游戏地图、图像像素
- 做矩阵运算(加减乘、转置)
- 实现简单的迷宫、围棋、俄罗斯方块等
接下来可以学一学:
- 三维数组:比如存储一个立体俄罗斯方块的形状,或者连续多张图片的序列。
- 动态二维数组:用指针或
vector<vector<int>>处理行数列数不固定的情况。 - 数组作为函数参数:把二维数组传给函数,方便重复使用。
二维数组是通向高级数据结构的起点,多练几道题,你会发现它其实特别顺手。
例题精讲
假设有一个二维数组 int arr[1024][1024],在内存中按行优先存储。下列哪种遍历方式的平均访问速度更快?
在C++中,二维数组名可以赋值给 int ** 类型的指针。
有 M 个学生,每人有 N 门课程的成绩,存放在二维数组 scores[M][N] 中。以下代码段计算每个学生的总分并存入 total 数组。请补充完整。
int total[M] = {0};
for (int i = 0; i < M; i++) {
int sum = 0;
for (int j = 0; j < N; j++) {
sum += ___;
}
total[i] = sum;
}给定一个 n×n 的灰度矩阵(用二维数组 int a[n][n] 表示),现要实现矩阵转置(a[i][j] 与 a[j][i] 交换)。下列哪个代码片段能正确完成转置且效率最高?
矩阵乘法:已知矩阵 A[M][N] 和矩阵 B[N][P],结果矩阵 C[M][P] 初始化为 0。以下代码实现 C = A × B,请补充内层累加语句。
for (int i = 0; i < M; i++) {
for (int j = 0; j < P; j++) {
for (int k = 0; k < N; k++) {
C[i][j] += ___ * B[k][j];
}
}
}