多維陣列
楊宗儒 @Sprout 2023
改寫自 Sprout 2022 講義
複習
一維陣列
int a[5]; // {?, ?, ?, ?, ?}
int b[5] = {1, 2, 3, 4, 5}; // {1, 2, 3, 4, 5}
int c[5] = {1, 2, 3}; // {1, 2, 3, 0, 0}
int d[5] = {}; // {0, 0, 0, 0, 0}
int e[5] = {0}; // {0, 0, 0, 0, 0}
一維陣列
int a[5];
a[0] = 21;
a[4] = 43;
cout << a[2] << endl; // ???
cout << a[5] << endl; // ???
二維陣列
想像我們想要存一個班級的座位表,
存下每個位置的學號
如果我們的座位長這樣呢?
為什麼一維陣列不太夠用?
一個維度不夠,就用兩個吧!
datatype name[size1][size2];
int arr2d[5][3];
char charr2d[2][10];
int A[4][3]
[0] | [1] | [2] | |
---|---|---|---|
A[0] | A[0][0] | A[0][1] | A[0][2] |
A[1] | A[1][0] | A[1][1] | A[1][2] |
A[2] | A[2][0] | A[2][1] | A[2][2] |
A[3] | A[3][0] | A[3][1] | A[3][2] |
初始化
int A[4][3] = {1};
int A[4][3]
[0] | [1] | [2] | |
---|---|---|---|
A[0] | 1 | 0 | 0 |
A[1] | 0 | 0 | 0 |
A[2] | 0 | 0 | 0 |
A[3] | 0 | 0 | 0 |
初始化
int A[4][3] = {1, 2, 3, 4};
int A[4][3]
[0] | [1] | [2] | |
---|---|---|---|
A[0] | 1 | 2 | 3 |
A[1] | 4 | 0 | 0 |
A[2] | 0 | 0 | 0 |
A[3] | 0 | 0 | 0 |
初始化
int A[4][3] =
{
{1, 2, 3},
{4, 5},
{6, },
{7, 8, 9}
};
[0] | [1] | [2] | |
---|---|---|---|
A[0] | 1 | 2 | 3 |
A[1] | 4 | 5 | 0 |
A[2] | 6 | 0 | 0 |
A[3] | 7 | 8 | 9 |
初始化
cout << A[2][2] << endl;
// 5
cout << A[3][1] << endl;
// 8
cout << A[0][3] << endl;
// ???
[0] | [1] | [2] | |
---|---|---|---|
A[0] | 1 | 2 | 3 |
A[1] | 4 | 5 | 0 |
A[2] | 6 | 0 | 0 |
A[3] | 7 | 8 | 9 |
遍歷
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
A[i][j] = ???;
}
}
for (int k = 0; k < 12; k++) {
A[k/4][k%3] = ???;
}
[0] | [1] | [2] | |
---|---|---|---|
A[0] | 1 | 2 | 3 |
A[1] | 4 | 5 | 0 |
A[2] | 6 | 0 | 0 |
A[3] | 7 | 8 | 9 |
(i, j) = (3, 1)
A[3][1] == 8
練習題
#OJ 1419
題解
題目:給定一個矩陣,輸出它的轉置
1 | 2 | 3 | 4 |
---|---|---|---|
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
1 | 5 | 9 |
---|---|---|
2 | 6 | 10 |
3 | 7 | 11 |
4 | 8 | 12 |
題解
觀察:第 (i, j) 個元素在轉置後會在哪裡?
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 5 | 6 | 7 | 8 |
2 | 9 | 10 | 11 | 12 |
0 | 1 | 2 | |
---|---|---|---|
0 | 1 | 5 | 9 |
1 | 2 | 6 | 10 |
2 | 3 | 7 | 11 |
3 | 4 | 8 | 12 |
5: (1, 0) -> (0, 1)
12: (2, 3) -> (3, 2)
(i, j) -> (j, i)
題解1
#include<iostream>
using namespace std;
int a[1000][1000];
int b[1000][1000];
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];
b[j][i] = a[i][j];
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cout << b[i][j];
if(j != n-1) cout << " ";
}
cout << endl;
}
}
a 是輸入
b 是輸出
在輸入 a 時把翻轉的結果存到 b
最後再輸出
題解2
#include<iostream>
using namespace std;
int a[1000][1000];
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];
b[j][i] = a[i][j];
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cout << a[j][i];
if(j != n-1) cout << " ";
}
cout << endl;
}
}
輸入完 a 之後直接輸出
多維陣列
多維陣列
int a[2][3][2];
就跟二維陣列一樣(?
指標!
小複習
- 在一維陣列的時候,我們提過陣列不加[]時是個指向陣列頭的指標
arr
0xFFF00A40
0xFFF00A36
char arr[0]
'A'
0xFFF00A40
'B'
0xFFF00A41
'C'
0xFFF00A42
'D'
0xFFF00A43
char arr[1]
char arr[2]
char arr[3]
- 二維也是!
&arr[0][0] + 0
arr[0][0]
arr[0][1]
arr[0][2]
arr[0][3]
arr[1][3]
arr[1][2]
arr[1][1]
arr[1][0]
&arr[0][0] + 1
&arr[0][0] + 2
&arr[0][0] + 3
&arr[0][0] + 4
&arr[0][0] + 5
&arr[0][0] + 6
&arr[0][0] + 7
名詞解釋
Row-major : 第 (i, j) 個元素在 &arr[0][0] + ( i * sizey + j )
Column-major : 第 (i, j) 個元素在 &arr[0][0] + ( j * sizex + i )
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 5 | 6 | 7 | 8 |
2 | 9 | 10 | 11 | 12 |
A[sizey][sizex]
C++ 的二維陣列是 row-major
另一種看法--二維指標
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 5 | 6 | 7 | 8 |
2 | 9 | 10 | 11 | 12 |
A[sizey][sizex]
arr
arr + 1
arr + 2
arr: int **
arr[0]: int*
int arr[3][4];
(arr+1)[0];
小練習
int arr[2][6] = {0};
for(int i = 0 ; i < 2; i++){
for(int j = 0 ; j < 6 ; j++){
arr[i][j] = i * 10 + j;
}
}
cout << **arr << " " << *arr << " " << arr << endl;
cout << *arr[0] << " " << arr[0] << endl;
cout << *(arr[0]+3) << " " << arr[0]+3 << endl;
cout << *(arr[1]+3) << " " << arr[1]+3 << endl;
cout << *(arr[0]+9) << " " << arr[0]+9 << endl;
小練習
int arr[2][6] = {0};
for(int i = 0 ; i < 2; i++){
for(int j = 0 ; j < 6 ; j++){
arr[i][j] = i * 10 + j;
}
}
cout << **arr << " " << *arr << " " << arr << endl;
cout << *arr[0] << " " << arr[0] << endl;
cout << *(arr[0]+3) << " " << arr[0]+3 << endl;
cout << *(arr[1]+3) << " " << arr[1]+3 << endl;
cout << *(arr[0]+9) << " " << arr[0]+9 << endl;
[0] | [1] | [2] | [3] | [4] | [5] | |
---|---|---|---|---|---|---|
arr[0] | 00 | 01 | 02 | 03 | 04 | 05 |
arr[1] | 10 | 11 | 12 | 13 | 14 | 15 |
禁忌/好習慣
禁忌1: 變數陣列大小
int n;
cin >> n;
int arr[n];
好習慣1: 常數陣列大小
const int SIZE = 100005;
int arr[SIZE];
- 題目通常會給陣列最大的範圍,直接開到那麼大就好
- 通常會再多開一點點,
可以逃避 index out of bound 的 bug
禁忌2: 函數內宣告大陣列
int main() {
int bigarr[1000][1000];
}
為什麼?
原因有點複雜,與程式如何存資料到記憶體有關
好習慣2: 在外面宣告大陣列
int bigarr[1000][1000];
int main() {
// TODO
}
存在 global 就不會讓陣列被存在stack,然後出現 stack overflow
最後一題
課內練習
OJ #999
- 行尾不空格
- 最後一行要換行
注意
題解
一個指針從起點開始走
什麼時候要換方向?
Ans. 邊界/填過數字
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 12 | 13 | 14 | 5 |
2 | 11 | 16 | 15 | 6 |
3 | 10 | 9 | 8 | 7 |
小技巧:把移動的方向存在一個陣列
int dir[4][2] = {
{0, 1},
{1, 0},
{-1, 0},
{0, -1}
};
資芽2023-二維陣列
By s0n9yu
資芽2023-二維陣列
- 128