二维数组 (2D Arrays) — AP Computer Science A CS A 学习指南
适合谁:AP Computer Science A 参加 AP Computer Science A 的考生。
覆盖内容:二维数组声明、行优先遍历、求和/搜索/翻转等常用操作模式、边界条件处理,以及游戏板、矩阵等实际应用场景。
前置知识:基础 Java 或任何其他过程式语言编程。
关于练习题:下文「练习题」一节的所有题目均为我们按 AP Computer Science A 风格编写的原创题目 (original problems),仅用于教学。它们不是 College Board 真题的复制,措辞、数值或语境可能不同。请把它们当作练手用;评分细则请对照 College Board 官方 mark scheme。
1. 什么是二维数组 (2D Arrays)?
二维数组是存储同类型数据的二维表格结构,本质是「数组的数组」,是AP CS A CED Unit 8的唯一核心考点。它由若干行 (row) 和列 (column) 组成,每个位置的元素 (element) 通过「行索引+列索引」唯一访问,索引默认从0开始计数。二维数组常被用来实现矩阵、游戏棋盘、学生成绩表等需要二维映射的场景,是AP CS A FRQ大题的高频考察载体。
2. 二维数组声明 (2D Array Declaration)
Java中二维数组的声明有三种常用形式,考官特别喜欢考察声明时行列参数的顺序、以及锯齿数组的定义规则:
- 指定行列数声明:适用于初始值未定的场景,第一个参数为行数,第二个为列数:
// 声明一个3行4列的整数数组,存储3个班级各4名学生的成绩
int[][] scores = new int[3][4];
- 直接初始化声明:适用于已知初始值的场景,大括号嵌套对应每行的元素:
// 声明并初始化3行3列的矩阵
int[][] matrix = {{1,2,3}, {4,5,6}, {7,8,9}};
- 锯齿数组 (ragged array) 声明:每行的列数可以不同,仅指定行数,后续单独给每行分配空间:
// 声明3行的锯齿数组,第一行2列,第二行3列,第三行4列
int[][] raggedArr = new int[3][];
raggedArr[0] = new int[2];
raggedArr[1] = new int[3];
raggedArr[2] = new int[4];
3. 行优先遍历 (Row-major Traversal)
行优先遍历是AP CS A要求的标准遍历方式,指先遍历完一行的所有列元素,再进入下一行遍历,和Java二维数组的内存存储顺序一致,遍历效率最高。
标准遍历代码(支持普通数组和锯齿数组):
// 外层循环控制行,i为行索引
for (int i = 0; i < matrix.length; i++) {
// 内层循环控制列,j为列索引,用matrix[i].length适配锯齿数组的不同列数
for (int j = 0; j < matrix[i].length; j++) {
// 访问元素 matrix[i][j]
System.out.print(matrix[i][j] + " ");
}
System.out.println(); // 每行结束换行
}
注意:如果仅需要读取元素不需要修改,可以使用增强for循环,但增强for循环对基本类型的元素是值拷贝,修改不会影响原数组,考官常在此设置陷阱题。
4. 常用操作模式:求和、搜索、翻转
这三类是AP考试中最常出现的二维数组操作模板,要求考生能快速写出无bug的代码:
① 求和
对数组中所有元素累加求和,公式为: 其中为行数,为第行的列数。
② 搜索
查找数组中是否存在目标值,找到后返回对应坐标,否则返回(-1,-1):
public static int[] search(int[][] arr, int target) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
if (arr[i][j] == target) return new int[]{i, j};
}
}
return new int[]{-1, -1};
}
③ 水平翻转
将每行的元素左右颠倒,交换位置j和n_i-1-j的元素即可,为第行的列数。
5. 边界条件 (Boundary Conditions)
二维数组的索引必须满足两个条件:,,违反则会抛出ArrayIndexOutOfBoundsException,这是选择题和FRQ的高频丢分点。
常见需要判断边界的场景:
- 访问数组边缘元素(第一行、最后一行、第一列、最后一列)
- 访问元素的上下左右相邻元素(比如扫雷游戏中统计周围地雷数量)
相邻元素访问的边界判断示例:
// 判断位置(i,j)的上方元素是否存在
if (i - 1 >= 0) { int up = arr[i-1][j]; }
// 判断位置(i,j)的右方元素是否存在
if (j + 1 < arr[i].length) { int right = arr[i][j+1]; }
6. 实际应用场景:游戏棋盘、矩阵运算
二维数组的实际应用是FRQ大题的常见出题背景,考生需要快速对应到基础操作:
① 游戏棋盘
比如3x3的井字棋 (Tic-Tac-Toe) 棋盘,用二维数组存储每个格子的状态('X'/'O'/'-'代表空):
char[][] ticTacToe = {{'-','-','-'}, {'-','-','-'}, {'-','-','-'}};
② 矩阵运算
- 矩阵加法:两个相同大小的矩阵对应位置元素相加,公式为
- 矩阵转置:将行和列互换,公式为,常用于图像处理、线性代数计算场景。
7. 常见陷阱 (Common Pitfalls)
- 错误做法:混淆行和列的索引顺序,把
arr[i][j]写成arr[j][i]。原因:对「数组的数组」本质理解不深,误以为第一个索引是列。正确做法:记住arr.length返回的是行数,arr[0].length返回的是第一行的列数,访问永远先写行索引再写列索引。 - 错误做法:遍历锯齿数组时用固定列数
arr[0].length作为内层循环上限。原因:默认所有行的列数相同,忽略锯齿数组的存在。正确做法:内层循环永远用arr[i].length作为当前行的列数上限。 - 错误做法:用增强for循环修改基本类型元素的值。原因:对Java的值传递机制不熟悉,以为增强for循环的变量是元素引用。正确做法:需要修改元素时用普通for循环通过索引访问。
- 错误做法:访问相邻元素时不做边界判断,导致数组越界。原因:忽略边缘元素没有完整的四个方向邻居。正确做法:每次访问非当前索引位置的元素前,都先判断索引是否在合法范围内。
8. 练习题 (AP Computer Science A 风格)
题1
题干:已知二维数组scores存储了多个班级的学生考试成绩,行数为班级数量,列数为对应班级的学生人数,请编写方法getClassAverage,参数为scores和班级索引classIndex,返回该班级的平均分,结果保留两位小数。
解答:
public static double getClassAverage(int[][] scores, int classIndex) {
int sum = 0;
int[] classScores = scores[classIndex];
for (int score : classScores) {
sum += score;
}
// 强制类型转换为double避免整数除法
return (double) sum / classScores.length;
}
说明:先获取对应班级的一维数组,遍历求和后除以班级人数即可,注意整数除法的陷阱。
题2
题干:给定n行n列的正方形矩阵matrix,请编写方法isSymmetric判断矩阵是否为对称矩阵(即对所有i,j满足matrix[i][j] == matrix[j][i])。
解答:
public static boolean isSymmetric(int[][] matrix) {
int n = matrix.length;
// 仅遍历上三角区域,避免重复判断
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (matrix[i][j] != matrix[j][i]) {
return false;
}
}
}
return true;
}
说明:不需要遍历所有i和j,仅判断上三角区域即可,时间复杂度为,比全遍历效率高一倍。
题3
题干:扫雷游戏中,给定row行col列的二维数组board,元素为1代表有地雷,0代表无地雷,请编写方法countMine,参数为board、坐标x和y,返回该坐标周围8个方向的地雷总数。
解答:
public static int countMine(int[][] board, int x, int y) {
int count = 0;
// 遍历8个方向的偏移量
int[][] dirs = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
// 边界判断+地雷判断
if (nx >=0 && nx < board.length && ny >=0 && ny < board[nx].length && board[nx][ny] == 1) {
count++;
}
}
return count;
}
9. 速查表 (Quick Reference Cheatsheet)
| 操作 | 核心规则/代码 |
|---|---|
| 二维数组声明 | int[][] arr = new int[行数][列数]; 第一个参数为行数 |
| 行优先遍历 | 外层循环控制行:for(int i=0;i<arr.length;i++),内层控制列:for(int j=0;j<arr[i].length;j++) |
| 边界判断 | 行索引范围:,列索引范围: |
| 矩阵转置 | 新矩阵newArr[j][i] = oldArr[i][j] |
| 增强for循环 | 仅用于读取元素,不能修改基本类型元素值 |
| 对称矩阵判断 | 仅遍历上三角区域,判断arr[i][j] == arr[j][i] |
10. 接下来怎么学
二维数组是AP CS A Unit 8的核心内容,后续会和ArrayList、递归等知识点结合考察,比如二维网格的DFS/BFS遍历、路径搜索等复杂算法题,也是FRQ大题的固定出题点之一,掌握本章的基础操作和避坑技巧,是攻克后续复杂考点的核心前提。
如果你在练习二维数组相关题目时遇到任何问题,随时可以到小欧提问,我们会为你提供针对性的讲解和练习指导。