多维数组
一、课上练习
编程练习
二、知识总结
✨ 多维数组的核心思想
在一维数组的学习中,我们把数组比作一排编了号的储物柜,用一个下标就能定位任意一个柜子。但如果数据天然具有"行和列"的结构——比如一张棋盘、一张成绩表、一幅像素图——用一排柜子来存就不够直观了。
多维数组就是数组的数组,可以用来存储矩阵、表格等多维数据。你可以把二维数组想象成一个有行有列的表格(或者教室里的座位),需要"第几排、第几列"两个编号才能确定一个位置。
| 列0 | 列1 | 列2 | |
|---|---|---|---|
| 行0 | arr[0][0] | arr[0][1] | arr[0][2] |
| 行1 | arr[1][0] | arr[1][1] | arr[1][2] |
不同维度数组的关系如下:
- 二维数组:一维数组的数组(表格)
- 三维数组:二维数组的数组(多层表格,如多张工作表)
- 四维数组:三维数组的数组
- 以此类推……
本节课主要学习二维数组,它是竞赛中最常用的多维数组。三维及更高维度的数组原理相同,只是多了更多的下标。
✨ 多维数组的定义
多维数组的定义语法如下:
type arrayName[size1][size2]...[sizeN];下面是不同维度数组的定义示例:
int arr2[2][3]; // 定义二维数组,2个大小为3的一维数组
int arr3[4][2][3]; // 定义三维数组,4个二维数组,每个二维数组中有2个大小为3的一维数组
int arr4[5][4][2][3]; // 定义四维数组,5个三维数组,每个三维数组中有4个二维数组✨ 多维数组的初始化
序列初始化
使用 {} 的方式对数组进行初始化,必须在定义数组时对其进行初始化。若 {} 内填写数据,则按照顺序对数组内元素进行赋值,未填写部分补 0。
1#include <iostream>
2using namespace std;
3
4int main() {
5 // 定义一个2x3的二维数组,并直接初始化
6 // 表示该二维数组中有2个大小为3的一维数组
7 // 每行为一个一维数组的初始化数据
8 int arr[2][3] = {
9 {1, 2, 3}, // 第一行
10 {4, 5, 6} // 第二行
11 };
12 return 0;
13}1#include <iostream>
2using namespace std;
3
4int main() {
5 // 定义一个2x2x3的三维数组,并直接初始化
6 // 表示该三维数组中有2个二维数组,每个二维数组中有2个大小为3的一维数组
7 // 每行为一个一维数组的初始化数据
8 int arr[2][2][3] = {
9 {
10 {1, 2, 3}, // 第一层第一行
11 {4, 5, 6} // 第一层第二行
12 },
13 {
14 {7, 8, 9}, // 第二层第一行
15 {10, 11, 12} // 第二层第二行
16 }
17 };
18 return 0;
19}fill_n 初始化
需要添加头文件 ,可以在任意位置对数组进行赋值,将数组元素全部设置为某一个值。使用 fill_n 需要填写三部分内容:数组的第一个元素地址、数据个数和初始化的数值,初始化的数值可以为任意值。
1#include <iostream>
2#include <algorithm>
3using namespace std;
4
5int main () {
6 int arr[2][3];
7 fill_n(&arr[0][0], 2 * 3, -1);
8 return 0;
9}memset 初始化
memset 是 C 语言风格的初始化方式,可以在任意位置对数组进行赋值,将数组元素全部设置为某一个值。
使用 memset 需要填写三部分内容:数组名称、初始化数值和数组大小。
其中初始化的数值一般使用十六进制表示,常用数值为:
0:全部置零-1:全部置 -10x3f:表示一个较大的整数(约 1061109567)0x7f:表示一个接近 int 最大值的整数
另外数组大小不是数组里面存储的数值的个数,而是占用内存的大小,通常使用 sizeof(arr) 获取。
1#include <iostream>
2#include <algorithm>
3using namespace std;
4
5int arr[105][105];
6
7int main () {
8 memset(arr, 0, sizeof(arr));
9 memset(arr, 1, sizeof(arr));
10 memset(arr, -1, sizeof(arr));
11 memset(arr, 0x3f, sizeof(arr));
12 memset(arr, 0x7f, sizeof(arr));
13 return 0;
14}将数组初始化为全 0 的几种方式
下面汇总了将二维数组初始化为全 0 的常用方法:
arr[2][3] = {};
arr[2][3] = {0};
arr[2][3] = {{}, {}};
fill_n(&arr[0][0], 2 * 3, 0);
memset(arr, 0, sizeof(arr));✨ 多维数组元素的访问
我们使用 数组名称[下标1][下标2][...] 的形式访问数组,可以通过该方式获取下标位置的元素值或者对该位置进行赋值。
1#include <iostream>
2using namespace std;
3
4int main() {
5 // 定义一个2x3的二维数组
6 int arr[2][3] = {
7 {1, 2, 3}, // 第一行
8 {4, 5, 6} // 第二行
9 };
10
11 // 访问和输出二维数组的元素
12 for (int i = 0; i < 2; ++i) {
13 for (int j = 0; j < 3; ++j) {
14 cout << arr[i][j] << " ";
15 }
16 cout << endl;
17 }
18
19 return 0;
20}1#include <iostream>
2using namespace std;
3
4int main() {
5 // 定义一个2x3x4的三维数组
6 int arr[2][3][4] = {
7 {
8 {1, 2, 3, 4},
9 {5, 6, 7, 8},
10 {9, 10, 11, 12}
11 },
12 {
13 {13, 14, 15, 16},
14 {17, 18, 19, 20},
15 {21, 22, 23, 24}
16 }
17 };
18
19 // 访问和输出三维数组的元素
20 for (int i = 0; i < 2; ++i) {
21 for (int j = 0; j < 3; ++j) {
22 for (int k = 0; k < 4; ++k) {
23 cout << arr[i][j][k] << " ";
24 }
25 cout << endl;
26 }
27 cout << endl;
28 }
29
30 return 0;
31}访问数组元素需要注意以下几点:
- 下标从 0 开始,到数组个数 - 1结束。即如果数组的元素个数为 n,第一个元素对应的下标为 0,最后一个元素对应的下标为 n - 1
- 数组下标不可以为负数,如果使用
arr[i-1]这样的操作时,一定要注意检查i-1是否大于等于 0 - 数组下标不可以越界使用,即如果数组的元素个数为 n,不能使用超过 n - 1 的下标。如果使用
arr[i+1]这样的操作时,一定要注意检查i+1是否小于 n - 对于越界使用数组下标的情况,编译器不会报错,但是程序运行时会出现错误,所以必须谨慎使用
✨ 多维数组的执行示例
下面通过具体例子展示二维数组在内存中的存储方式和操作过程。
例1:二维数组的内存布局
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};虽然我们把二维数组想象成一个"表格",但在内存中它是按行连续存储的:
逻辑视图(表格):
+---+---+---+
| 1 | 2 | 3 | row 0
+---+---+---+
| 4 | 5 | 6 | row 1
+---+---+---+内存中的实际存储(连续):
+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 |
+---+---+---+---+---+---+
[0][0] [0][1] [0][2] [1][0] [1][1] [1][2]arr[i][j] 在内存中的位置 = 起始地址 + (i 列数 + j) sizeof(int)例2:矩阵转置的执行过程
将 2x3 矩阵转置为 3x2 矩阵,即行列互换:
arr[2][3]:
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 4 | 5 | 6 |
+---+---+---+result[3][2]:
1+---+---+
2| 1 | 4 |
3+---+---+
4| 2 | 5 |
5+---+---+
6| 3 | 6 |
7+---+---+转置的核心操作:result[j][i] = arr[i][j]
1arr[0][0]=1 → result[0][0]=1
2arr[0][1]=2 → result[1][0]=2
3arr[0][2]=3 → result[2][0]=3
4arr[1][0]=4 → result[0][1]=4
5arr[1][1]=5 → result[1][1]=5
6arr[1][2]=6 → result[2][1]=6例3:使用不同方式初始化数组的效果
int arr[2][3] = {}; → 全部为0:{{0,0,0},{0,0,0}}
int arr[2][3] = {{1,2},{3}}; → 部分初始化:{{1,2,0},{3,0,0}}
fill_n(&arr[0][0], 6, -1); → 全部为-1:{{-1,-1,-1},{-1,-1,-1}}
memset(arr, 0x3f, sizeof(arr)); → 全部为大数:约1061109567✨ 多维数组的解题步骤
使用二维数组解决问题的一般步骤:
- 确定数组维度:根据题目确定行数和列数,数组大小要比题目给出的最大范围稍大一些
- 初始化数组:根据需要选择合适的初始化方式(全0用
= {},其他值用fill_n或memset) - 读入数据:使用双重循环读入二维数组的元素
- 处理数据:根据题目要求进行遍历、查找、计算等操作
- 输出结果:使用双重循环输出结果矩阵
遍历二维数组的标准写法:
1// 读入 n 行 m 列的二维数组
2for (int i = 0; i < n; ++i) {
3 for (int j = 0; j < m; ++j) {
4 cin >> arr[i][j];
5 }
6}
7
8// 输出 n 行 m 列的二维数组
9for (int i = 0; i < n; ++i) {
10 for (int j = 0; j < m; ++j) {
11 cout << arr[i][j];
12 if (j + 1 < m) cout << " "; // 每行元素之间用空格分隔
13 }
14 cout << endl;
15}✨ 多维数组的常见错误
- 数组下标越界:二维数组
arr[n][m]的合法下标范围是0~n-1和0~m-1,访问arr[n][0]或arr[0][m]都是越界行为 - 行列混淆:在处理矩阵时容易搞混行数和列数,建议用有意义的变量名如
rows和cols - 函数传参忘记指定列数:二维数组作为函数参数时,第二维的大小必须明确指定,如
void f(int arr[][100], int n, int m) - memset 初始化为任意值:
memset是按字节赋值的,只能可靠地初始化为0、-1、0x3f、0x7f等特殊值,不能用来初始化为 1 或其他任意整数(memset(arr, 1, sizeof(arr))不会把每个元素设为 1) - 大数组定义在主函数内:大的二维数组(如
int arr[1000][1000])应该定义在主函数外面作为全局变量,否则会栈溢出
✨ 多维数组函数传参
在传递多维数组给函数时,除了第一个维度外,其他维度的大小必须在函数参数中明确指定。注意这里的大小要是整个数组的大小。
1#include <iostream>
2using namespace std;
3
4// 打印二维数组的函数
5void print2DArray(int arr[][3], int rows, int colums) {
6 for (int i = 0; i < rows; ++i) {
7 for (int j = 0; j < colums; ++j) {
8 cout << arr[i][j] << " ";
9 }
10 cout << endl;
11 }
12}
13
14int main() {
15 int arr[2][3] = {
16 {1, 2, 3},
17 {4, 5, 6}
18 };
19
20 print2DArray(arr, 2, 3);
21 return 0;
22}1#include <iostream>
2using namespace std;
3
4// 打印三维数组的函数
5void print3DArray(int arr[][3][4], int size1, int size2, int size3) {
6 for (int i = 0; i < size1; ++i) {
7 for (int j = 0; j < size2; ++j) {
8 for (int k = 0; k < size3; ++k) {
9 cout << arr[i][j][k] << " ";
10 }
11 cout << endl;
12 }
13 cout << endl;
14 }
15}
16
17int main() {
18 int arr[2][3][4] = {
19 {
20 {1, 2, 3, 4},
21 {5, 6, 7, 8},
22 {9, 10, 11, 12}
23 },
24 {
25 {13, 14, 15, 16},
26 {17, 18, 19, 20},
27 {21, 22, 23, 24}
28 }
29 };
30
31 print3DArray(arr, 2, 3, 4);
32 return 0;
33}