交错数组和二维数组有什么区别?
简化版
二维数组通常表示规则矩阵,行列大小固定;交错数组是“数组的数组”,每一行长度可以不同。规则矩阵适合图像、棋盘、矩阵计算;交错数组适合每行长度不一致的数据,但访问时要小心空行、行长度不同和缓存局部性。
详细版
例如 3 行 4 列矩阵可以看成规则二维数组:
3 x 4
row0: 4 elements
row1: 4 elements
row2: 4 elements
交错数组可以是:
row0: 2 elements
row1: 5 elements
row2: 1 element
面试要说明:很多语言里的 int[][] 实际是数组的数组,行对象可能不连续;真正连续的二维矩阵常按行主序或列主序展平存储。选择时看数据是否规则、是否需要高性能遍历、是否允许每行长度不同。
完整版教学
一、二维数组表达规则网格
二维数组最常见用途是矩阵、棋盘、图像像素、动态规划表。它的直觉是每个位置都有行号和列号。
matrix[3][4]
(0,0) (0,1) (0,2) (0,3)
(1,0) (1,1) (1,2) (1,3)
(2,0) (2,1) (2,2) (2,3)
规则二维数组的特点是每一行列数一致,适合用 rows * cols 估算空间。
记忆钩子:二维数组像矩形表,交错数组像每行长短不同的表。
二、交错数组是数组的数组
交错数组的每一行本身也是一个数组,因此每行长度可以不同。
jagged:
row0 -> [1, 2]
row1 -> [3, 4, 5, 6, 7]
row2 -> [8]
这种结构适合邻接表、分组结果、每个用户的不同数量标签等不规则数据。
如果强行用规则二维数组存这些数据,就会有大量空位。
max cols = 5
row0 wastes 3 slots
row1 wastes 0 slots
row2 wastes 4 slots
三、内存布局可能完全不同
在 C 这类语言里,规则二维数组可以连续存储;而 Java 的 int[][] 是外层数组保存内层数组引用,行之间不一定连续。
outer -> row0 -> [1, 2, 3]
-> row1 -> [4, 5, 6]
-> row2 -> [7, 8, 9]
如果需要严格连续内存,可以用一维数组模拟二维:
index = row * cols + col
例如 rows=3, cols=4,位置 (2,1) 的一维下标是 2 * 4 + 1 = 9。
四、行主序和列主序影响遍历性能
连续二维数组通常有行主序或列主序。行主序表示同一行的元素连续存储。
row-major:
a[0][0], a[0][1], a[0][2], a[1][0], ...
如果按行遍历,就更符合内存连续访问;如果按列跳着访问,缓存局部性会差一些。
按行: (0,0)->(0,1)->(0,2)->(0,3)
按列: (0,0)->(1,0)->(2,0)->...
矩阵计算、图像处理里,这类访问顺序差异会明显影响性能。
五、边界检查要按每一行长度
交错数组不能假设每行长度都一样。
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
// 访问 a[i][j]
}
}
如果写成 j < a[0].length,遇到短行就可能越界,遇到长行又会漏数据。
还要小心某一行可能是 null,尤其在手动构造或部分初始化时。
六、怎么选择更合适
| 结构 | 适合场景 | 优点 | 风险 |
|---|---|---|---|
| 规则二维数组 | 矩阵、棋盘、DP 表 | 下标规则,计算简单 | 不规则数据浪费空间 |
| 交错数组 | 邻接表、分组列表 | 每行可变,空间灵活 | 行长度不同,边界复杂 |
| 一维展平数组 | 高性能矩阵 | 连续紧凑,缓存友好 | 下标换算更容易写错 |
面试里不要只说“一个规则一个不规则”,还要提内存布局和访问方式。
七、常见误区与追问
- 误区:所有语言的二维数组都是连续内存。 有些语言是数组的数组,行之间可能不连续。
- 误区:交错数组每行长度一样。 交错数组允许每行长度不同,遍历必须看当前行长度。
- 误区:二维数组一定比一维数组慢。 真正连续布局下,性能关键是访问顺序和缓存局部性。
- 追问:如何用一维数组模拟二维数组? 行主序常用
index = row * cols + col。 - 追问:交错数组适合什么场景? 每行元素数不同的数据,如邻接表、分组结果、稀疏行。
- 追问:为什么按行遍历通常更快? 行主序下相邻列连续存储,更容易命中 cache。
八、加强记忆
二维数组和交错数组按“规则矩形 vs 不规则行”来记。规则二维数组适合矩阵、棋盘和 DP 表,交错数组适合每行长度不同的数据。性能层面要看真实内存布局:连续矩阵可以用 row * cols + col 展平,按行遍历更 cache 友好;交错数组遍历必须按每一行自己的长度检查边界。