Двумерные массивы

Во многих задачах нужно работать не с одной строкой чисел, а с таблицей: оценки по предметам (класс × предмет), игровое поле, матрица чисел, карта с высотами.

Двумерный массив можно представлять как таблицу или сетку:

int[][] a = new int[3][4];  // 3 строки, 4 столбца

У такого массива есть элементы a[0][0], a[0][1], …, a[2][3].


Основное

Двумерный массив целых чисел:

int[][] a = new int[n][m];   // n строк, m столбцов

a[i][j] = 10;                 // записать значение в ячейку (i, j)
int x = a[i][j];              // прочитать значение

int rows = a.length;          // количество строк
int cols = a[0].length;       // количество столбцов в первой строке

Важно:

  • индексы по строкам: от 0 до a.length - 1;
  • индексы по столбцам: от 0 до a[i].length - 1;
  • новый int[][] по умолчанию заполнен нулями.

Идея двумерного массива

Один массив — это «линейка» значений:

int[] line = new int[5];  // 5 чисел подряд

Двумерный массив — это «таблица» или «поле»:

int[][] a = new int[3][4]; // 3 строки, 4 столбца

Можно думать о нём как об «массиве массивов»:

  • a[0] — первая строка (массив int[]);
  • a[1] — вторая строка;
  • a[i][j] — элемент в i‑й строке и j‑м столбце.

Объявление и создание двумерного массива

Общий шаблон:

тип[][] имя = new тип[числоСтрок][числоСтолбцов];

Примеры:

int n = 3;
int m = 4;

int[][] a = new int[n][m];         // таблица n × m из целых
double[][] d = new double[5][5];   // 5 × 5 вещественных
boolean[][] used = new boolean[n][m]; // поле посещённых клеток

Можно создать и заполнить массив сразу литералами:

int[][] table = {
    {1, 2, 3},
    {4, 5, 6}
};
// 2 строки, 3 столбца

Чтение двумерного массива из ввода

Часто во входе сначала даны размеры n и m, затем n × m чисел.

import java.util.Scanner;

public class Read2DArrayExample {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        int n = in.nextInt();          // число строк
        int m = in.nextInt();          // число столбцов

        int[][] a = new int[n][m];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                a[i][j] = in.nextInt();
            }
        }

        // вывод массива в виде таблицы
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                System.out.print(a[i][j] + " ");
            }
            System.out.println();
        }
    }
}

Ввод
2 3
1 2 3
4 5 6
Вывод
1 2 3
4 5 6

Здесь внешний цикл идёт по строкам (индекс i), внутренний — по столбцам (индекс j).


Сумма всех элементов

int n = in.nextInt();
int m = in.nextInt();
int[][] a = new int[n][m];

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        a[i][j] = in.nextInt();
    }
}

int sum = 0;

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        sum = sum + a[i][j];
    }
}

System.out.println(sum);

Суммы по строкам и столбцам

Суммы по строкам

int n = in.nextInt();
int m = in.nextInt();
int[][] a = new int[n][m];

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        a[i][j] = in.nextInt();
    }
}

for (int i = 0; i < n; i++) {
    int rowSum = 0;
    for (int j = 0; j < m; j++) {
        rowSum = rowSum + a[i][j];
    }
    System.out.println(rowSum);
}

Суммы по столбцам

int n = in.nextInt();
int m = in.nextInt();
int[][] a = new int[n][m];

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        a[i][j] = in.nextInt();
    }
}

for (int j = 0; j < m; j++) {
    int colSum = 0;
    for (int i = 0; i < n; i++) {
        colSum = colSum + a[i][j];
    }
    System.out.println(colSum);
}

Двумерный массив как игровое поле

Двумерный массив удобно использовать для представления игрового поля:
0 — пустая клетка, 1 — стена, 2 — персонаж и т.п.

int n = in.nextInt();
int m = in.nextInt();
int[][] field = new int[n][m];

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        field[i][j] = in.nextInt();
    }
}

Пример проверки соседних клеток (аккуратно с границами):

int i = in.nextInt();
int j = in.nextInt();

// проверяем, есть ли клетка сверху
if (i > 0) {
    int up = field[i - 1][j];
}

// снизу
if (i + 1 < n) {
    int down = field[i + 1][j];
}

Что нужно запомнить

Двумерный массив можно представлять как таблицу n × m:

int[][] a = new int[n][m];

Первый индекс — номер строки, второй — номер столбца: a[i][j].

Размеры: a.length — количество строк, a[i].length — количество столбцов в строке i.
Типичный приём — вложенные циклы for по строкам и столбцам для чтения, обработки и вывода таблицы.

Заполнение двумерной таблицы

Под таблицей будем понимать набор из n × m элементов, структурированный в n строк и m столбцов. К каждому элементу обращаемся по номеру строки и столбца.

Например, таблица 5 × 6, заполненная числами от 0 до 29:

0   1   2   3   4   5
6   7   8   9  10  11
12 13  14  15  16  17
18 19  20  21  22  23
24 25  26  27  28  29

Значение элемента во второй строке и третьем столбце (индексация с нуля) равно 15.

Для работы с таблицами в C++ удобно использовать «вектор векторов»:

vector<vector<int>> t(n, vector<int>(m, 0));

После этого получаем таблицу t из n строк и m столбцов, заполненную нулями.

Заполнение по порядку через отдельный счётчик:

int c = 0;
for (int i = 0; i < n; ++i){
    for (int j = 0; j < m; ++j){
        t[i][j] = c;
        ++c;
    }
}

То же можно сделать без переменной c:

t[i][j] = i * m + j;

Вывод таблицы:

for (int i = 0; i < n; ++i){
    for (int j = 0; j < m; ++j){
        cout << t[i][j] << " ";
    }
    cout << endl;
}

Задача о заполнении таблицы змейкой

Нужно заполнить таблицу змейкой: строки с чётными индексами слева направо, строки с нечётными индексами справа налево.

Для 5 × 6 это выглядит так:

0   1   2   3   4   5
11 10  9   8   7   6
12 13  14  15  16  17
23 22  21  20  19  18
24 25  26  27  28  29

Решение:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    vector<vector<int>> t(n, vector<int>(m));
    int now = 0;
    for (int i = 0; i < t.size(); ++i){ // число строк — t.size()
        if (i % 2 == 0){
            for (int j = 0; j < t[i].size(); ++j){ // число элементов в i-й строке — t[i].size()
                t[i][j] = now;
                ++now;
            }
        } else {
            for (int j = t[i].size() - 1; j >= 0; --j){
                t[i][j] = now;
                ++now;
            }
        }
    }
    for (int i = 0; i < t.size(); ++i){
        for (int j = 0; j < t[i].size(); ++j){
            cout << t[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

Ввод
2 3
Вывод
0 1 2
5 4 3

Работа с двумерной таблицей

Задача об обмене столбцов таблицы:

Дана таблица размера n × m. Нужно поменять местами столбцы с номерами p и q.

Решение:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int n, m, p, q;
    cin >> n >> m >> p >> q;
    vector<vector<int>> t(n, vector<int>(m));
    for (int i = 0; i < t.size(); ++i){
        for (int j = 0; j < t[i].size(); ++j){
            cin >> t[i][j];
        }
    }
    for (int i = 0; i < n; ++i){
        swap(t[i][p], t[i][q]);
    }
    for (int i = 0; i < t.size(); ++i){
        for (int j = 0; j < t[i].size(); ++j){
            cout << t[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

Задача о симметричной таблице:

Нужно проверить, симметрична ли квадратная таблица относительно главной диагонали.

Пример симметричной таблицы:

1 2 3 4
2 3 1 2
3 1 5 9
4 2 9 0

Решение:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int n;
    cin >> n;
    vector<vector<int>> t(n, vector<int>(n));
    for (int i = 0; i < t.size(); ++i){
        for (int j = 0; j < t[i].size(); ++j){
            cin >> t[i][j];
        }
    }
    bool issym = true;
    for (int i = 0; i < t.size(); ++i){
        for (int j = 0; j < i; ++j){
            if (t[i][j] != t[j][i]){
                issym = false;
            }
        }
    }
    if (issym){
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
    return 0;
}

По материалам:
М. Густокашин. Введение в программирование на языке C++. Сириус Курсы. Перейти к курсу.

Двумерный массив – это список списков (таблица \(n \times m\)). Важно правильно создавать такую таблицу.

Основное

n = 3
m = 4

a = [[0] * m for _ in range(n)]

a[0][0] = 10
x = a[0][0]

rows = len(a)
cols = len(a[0])

Если написать a = [[0] * m] * n, все строки будут ссылаться на один и тот же список, и изменения будут «размножаться» по всем строкам.

Чтение таблицы из ввода

n, m = map(int, input().split())

a = []
for _ in range(n):
    row = list(map(int, input().split()))
    a.append(row)

Вывод таблицы

for i in range(n):
    for j in range(m):
        print(a[i][j], end=' ')
    print()

В Java int[][] – это «массив массивов»: строки могут быть разной длины. В C++ и Python идея такая же: это тоже «таблица из строк», и строки тоже могут быть разной длины.

В Java новый int[][] сразу заполнен нулями. В C++ и Python это зависит от того, как создаёшь: если создать таблицу с начальными нулями – будут нули, если просто объявить и потом заполнять – значения появятся только после ввода.

В Java размеры удобно брать через a.length и a[i].length. В C++ – через a.size() и a[i].size(). В Python – через len(a) и len(a[i]).

Ошибка выхода за границы в Java и C++ обычно приводит к аварийному завершению (или странному поведению в C++). В Python при неверном индексе будет IndexError.

В Python важно правильно создавать таблицу, чтобы строки были независимыми: [[0] * m for _ in range(n)].