CC++ & Algorithm

C++二维数组经典应用

较难19
语言版本:C++Python
概述:用二维数组解决实际问题,比如学生成绩统计、图像处理中的灰度矩阵。

二维数组的经典应用:从成绩统计到图像处理

前面我们学会了如何定义和遍历一个二维数组,就像有了一个能存表格的神奇工具。那这个工具能在生活中派上什么用场呢?其实到处都是——班级成绩表、游戏地图、照片的像素、超市的货架……只要数据有行有列,就能用二维数组装进来。这一篇我们就来看两个最常用的场景,顺便学一些实战技巧。

应用1:学生成绩统计——按行求平均分

假设老师把3个学生、每人4门课的成绩记在一张表里,就像这样:

学生\科目语文数学英语科学
学生185927890
学生288769582
学生370859088

这张表在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(学生)。代码逻辑完全对称,只是把行和列角色互换。

常见错误提醒

  1. 求和时忘记把 sum 放在外层循环里面,导致第二个学生的总分包含了第一个学生的成绩。
  2. sum / 4 而不是 sum / 4.0,结果被截断成整数,比如 85+92+78+90=345,345/4=86(正确应该是86.25)。
  3. 数组下标写反了,比如 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”形状。这种用二维数组表示图片的方法,就是最基础的图像处理——灰度图。现实中的照片有几百甚至几千万个像素,但原理完全一样。

常见错误提醒

  1. 打印时忘记用 cout << endl; 换行,结果所有像素挤在一行里,看不出图案。
  2. 把256(颜色范围是0~255)错当成255使用,或者把0和255理解反了。
  3. 给数组赋值时少写了花括号或者多写了逗号,导致编译错误。

更多玩法:矩阵运算与查找

二维数组还能做很多事,比如:

  • 矩阵加法:两个同样大小的数组,对应位置相加。
  • 矩阵转置:把行和列互换。
  • 查找最大值:遍历整个数组,记录最大值及其位置。
  • “扫雷”游戏:用二维数组存放地雷,每个格子统计周围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>> 处理行数列数不固定的情况。
  • 数组作为函数参数:把二维数组传给函数,方便重复使用。

二维数组是通向高级数据结构的起点,多练几道题,你会发现它其实特别顺手。

例题精讲

1单选题

假设有一个二维数组 int arr[1024][1024],在内存中按行优先存储。下列哪种遍历方式的平均访问速度更快?

A按行遍历(外层循环行,内层循环列)
B按列遍历(外层循环列,内层循环行)
C两种方式速度相同
D取决于编译器的优化级别
2判断题

在C++中,二维数组名可以赋值给 int ** 类型的指针。

3填空题
有 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;
}
4单选题

给定一个 n×n 的灰度矩阵(用二维数组 int a[n][n] 表示),现要实现矩阵转置(a[i][j] 与 a[j][i] 交换)。下列哪个代码片段能正确完成转置且效率最高?

Afor (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { int t = a[i][j]; a[i][j] = a[j][i]; a[j][i] = t; }
Bfor (int i = 0; i < n; i++) for (int j = i+1; j < n; j++) { int t = a[i][j]; a[i][j] = a[j][i]; a[j][i] = t; }
Cfor (int i = 0; i < n; i++) for (int j = 0; j < i; j++) { int t = a[i][j]; a[i][j] = a[j][i]; a[j][i] = t; }
Dfor (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (i != j) { int t = a[i][j]; a[i][j] = a[j][i]; a[j][i] = t; }
5填空题
矩阵乘法:已知矩阵 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];
        }
    }
}