数组(一维与二维)

本节主线

数组用一个名字保存一批同类数据,用下标找到其中一个。一维数组像一排盒子,二维数组像有行有列的座位表。这节课最终要稳定做到:下标不越界,循环范围与数组范围一致。

先看一个教室座位表

一排 4 名学生的分数可以用 score[j] 保存。如果有 3 排,就需要同时说清“第几排、第几个”,也就是 score[i][j]

i / 列 j0123
086917895
188839076
292858994

例如 score[1][2]=90:第一个下标选行,第二个下标选列。遍历整张表时,外层循环换行,内层循环沿当前行逐列访问。

认知定位

  • 建模对象:同类数据组成的序列或方格。
  • 算法范式:顺序遍历与模拟。
  • 维护信息:数组元素、有效下标范围、当前行列。
  • 压缩信息:不再为每个数据单独命名一个变量。
  • 学生易卡点:Implementation,容易混淆“元素个数”和“最后下标”。

第一部分:定义数组

int a[5];   // 定义 5 个 int,名字叫 a

三个要点:

  1. int元素类型a数组名5大小(能装几个)。
  2. 大小必须是整数或整数常量表达式,不能用变量。
  3. 数组在内存里是连续的一整块。

要用变量般的大小,请用常量:

const int maxn = 1001;
int a[maxn];        // 推荐写法
 
#define N 1001
int a[N];           // 老式写法,也可以

第二部分:下标(索引)

数组用下标访问每个元素,从 0 开始

int a[5];
       a[0]  a[1]  a[2]  a[3]  a[4]
  • a[0] 是第 1 个,a[4] 是第 5 个(最后一个)。
  • 竞赛小技巧:把数组开大一点、舍弃 a[0]、从 a[1] 开始用,这样”a[i] 就是第 i 个”,下标和题意对得上,少出错。

⚠️ 下标越界很危险:编译器不检查下标是否有效。写 a[5](越界)能编译通过,但运行时可能破坏别的数据甚至崩溃。开够大小是基本功。


第三部分:初始化

int a[4];                       // 不初始化,里面是随机值(垃圾值)
 
int a[4] = {20, 345, 700, 22};  // 定义同时逐个赋值
 
int a[10] = {12, 19, 22};       // 只给前 3 个赋值,后 7 个自动补 0
 
int a[10] = {1};                // 思考:a[0]=1,其余 9 个都是 0!
int a[10] = {0};                // 常用:整个数组清零

记住规则:花括号里给了的就用给的值,没给到的一律补 0
所以 {1} 不是”全 1”,而是”第一个是 1,其余是 0”。要全 1 只能一个个写。


第四部分:遍历数组(配合循环)

数组和 for 循环是黄金搭档:

int n;
cin >> n;
int a[1005];
for (int i = 1; i <= n; i++) cin >> a[i];   // 读入 n 个数(从 a[1] 开始)
 
int sum = 0;
for (int i = 1; i <= n; i++) sum += a[i];   // 求和
cout << sum;

第五部分:二维数组

存”表格 / 矩阵”这类有行有列的数据:

int g[3][4];        // 3 行 4 列
g[i][j]             // 第 i 行、第 j 列

遍历要用嵌套循环(呼应循环那一课”外层管行、内层管列”):

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        cin >> g[i][j];
    }
}

小结

  • 数组 = 一排连续的同类型盒子,一个名字 + 下标访问。
  • 下标从 0 开始;越界不报错但危险,数组要开够大。
  • 初始化规则:没给到的补 0;{0} 清零,{1} 只有首位是 1。
  • 一维配单层 for,二维配嵌套 for。

课后小练

题目练习点
P1427 小鱼的数字游戏一维数组读入与逆序输出
P5731 蛇形方阵 · 题解二维数组、坐标与方向模拟
P5732 杨辉三角二维数组与相邻元素递推

衔接:学完数组,下一步常接选择排序等简单排序 —— 见排序专题。