跳转至

二维数组

二维数组的定义

一维数组像一排格子,二维数组像一张表,有行、有列。

int a[110][110];

开了 110 行、110 列,一共能存 110 × 110 个 int。

数组的访问

先行,后列。

a[0][0] = 5; // 第0行,第0列
a[2][3] = 8; // 第2行,第3列

这里的行列下标,也都是从 0 开始。

下面的例子统一处理 n 行 m 列,1 <= n, m <= 100,从下标 0 开始存。

实际数据用到 a[0][0] 到 a[n-1][m-1]。

数组的初始化

数组开全局,默认都是 0

int a[110][110];

局部数组,也可以用大括号清零

int a[110][110] = {};

普通局部数组如果不初始化,不能直接读取元素值。

使用两层 for 循环赋值

for (int i = 0; i < n; i++)
    for (int j = 0; j < m; j++) a[i][j] = 0;

这里只清零实际使用的 n 行 m 列,不是整个数组容量。

使用大括号给出初始值

int a[4][5] = {1, 2, 3, 4, 5, 6, 7, 8};

按行依次填,剩余位置补 0:

1 2 3 4 5
6 7 8 0 0
0 0 0 0 0
0 0 0 0 0

每行也可以单独用大括号:

int a[4][5] = {{1}, {2, 3}, {4, 5, 6}, {7, 8}};

每行没写够的部分补 0:

1 0 0 0 0
2 3 0 0 0
4 5 6 0 0
7 8 0 0 0

数组的输入输出

外层管行,内层管列。一行输出完,再换行。

#include <iostream>

using namespace std;

int a[110][110];

int main() {
    int n, m;
    cin >> n >> m;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++) cin >> a[i][j];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) cout << a[i][j] << ' ';
        cout << '\n';
    }

    return 0;
}

注意输出换行,这里经常出错。

一行输出完,再换行。cout << '\n'; 写在内层循环外面、外层循环里面。

  • 漏写换行,所有数据会挤在同一行。
  • 把换行写进内层循环,会变成每输出一个数就换一行。

数组的遍历

逐行遍历,先走完一行,再换下一行。上面的输出就是这种写法。

逐列遍历,先走完一列,再换下一列。

for (int j = 0; j < m; j++) {
    for (int i = 0; i < n; i++) cout << a[i][j] << ' ';
    cout << '\n';
}

注意:这里每输出完一列,就换行,不是按原表格的行来输出。

用常量开数组

const int N = 1e3 + 10;
int a[N][N];

二维数组,容量要把行数、列数乘起来算。

常见环境中,一个 int 是 4 字节,1000 × 1000 个 int 约占 4 MB。

能开多大,还要看题目的内存限制、其他数据的占用,以及数组放在全局还是局部。

较大的数组,优先开全局,但开全局也不能突破内存限制。

常见错误

行列写反

n 行 m 列,行下标用 i < n,列下标用 j < m。

测试时,除了正方形,还要试一试 2 行 3 列,行列写反才容易暴露出来。

一维越界,也是越界。

int a[3][4]; 的行下标是 0~2,列下标是 0~3。

即使内存看起来挨着,也不能用 a[0][4] 代替 a[1][0]。

memset(先不看)

需要头文件 <cstring>。

memset(a, 0, sizeof a); // 将整个数组清零

-1、0x3f 的用法与一维数组相同,别把 memset 当成逐个整数赋值。