Вложенные циклы — таблица умножения, матрицы, сложность O(n×m), паттерны, вложенные for+if
Вложенные циклы — это циклы внутри циклов. Они используются для обработки двумерных структур: таблиц, матриц, паттернов из символов.
Что такое вложенные циклы?
Вложенные циклы — это когда один цикл находится внутри другого. Внешний цикл определяет «строки», а внутренний — «столбцы». Это как таблица: строки × столбцы.
Аналогия: Представьте таблицу умножения. Внешний цикл перебирает строки (1, 2, 3...), а внутренний — столбцы (×1, ×2, ×3...). Каждая ячейка таблицы — это результат одной итерации внутреннего цикла.
for (внешний_счётчик) {
for (внутренний_счётчик) {
// тело внутреннего цикла
}
// код после внутреннего цикла (выполняется после каждой строки)
}
Важно понимать: на каждую итерацию внешнего цикла внутренний цикл выполняется полностью.
Пример: таблица умножения
Выведем таблицу умножения для чисел 1–5 на 1–5:
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
System.out.print(i * j + "\t");
}
System.out.println();
}
Вывод:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
\t — это символ табуляции (отступ). Он выравнивает столбцы.
Как это работает:
- Внешний цикл:
i = 1 - Внутренний цикл полностью:
j = 1, 2, 3, 4, 5→ печатаем 1, 2, 3, 4, 5 System.out.println()— перевод строки- Внешний цикл:
i = 2 - Внутренний:
j = 1, 2, 3, 4, 5→ 2, 4, 6, 8, 10 - ... и так далее
Производительность: O(n × m)
Временная сложность вложенных циклов — O(n × m), где n — количество итераций внешнего цикла, m — внутреннего.
В примере выше: 5 × 5 = 25 итераций. Если внешний цикл — 1000, внутренний — 1000, то всего 1 000 000 итераций. Это быстро для компьютера, но заметьте: при 10 000 × 10 000 будет 100 000 000 итераций — уже может быть медленно.
Правило: вложенные циклы — мощный инструмент, но будьте осторожны с объёмом данных. Если n и m велики — ищите способ сделать задачу эффективнее.
Практика: матрица (двумерный массив)
Вложенные циклы идеально подходят для работы с матрицами — двумерными структурами данных:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + "\t");
}
System.out.println();
}
Вывод:
1 2 3
4 5 6
7 8 9
matrix[i][j] — обращение к элементу в строке i, столбце j.
Практика: сумма элементов матрицы
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int sum = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
sum += matrix[i][j];
}
}
System.out.println("Сумма всех элементов: " + sum);
Вывод:
Сумма всех элементов: 45
Практика: поиск максимума в матрице
int[][] matrix = {
{3, 7, 2},
{8, 1, 9},
{4, 6, 5}
};
int max = matrix[0][0];
int maxRow = 0;
int maxCol = 0;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] > max) {
max = matrix[i][j];
maxRow = i;
maxCol = j;
}
}
}
System.out.println("Максимум: " + max);
System.out.println("На позиции: строка " + maxRow + ", столбец " + maxCol);
Вывод:
Максимум: 9
На позиции: строка 1, столбец 2
Паттерны из символов
Вложенные циклы позволяют создавать «картинки» из символов. Давайте создадим несколько паттернов:
Прямоугольник:
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++) {
System.out.print("* ");
}
System.out.println();
}
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
Паттерн: лесенка
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("* ");
}
System.out.println();
}
*
* *
* * *
* * * *
* * * * *
Ключевая идея: внутренний цикл выполняется i раз. На первой строке (i=1) — 1 звёздочка, на второй (i=2) — 2, и т.д.
Паттерн: перевёрнутая лесенка
for (int i = 5; i >= 1; i--) {
for (int j = 1; j <= i; j++) {
System.out.print("* ");
}
System.out.println();
}
* * * * *
* * * *
* * *
* *
*
Паттерн: цифровой прямоугольник
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= 5; j++) {
System.out.print(i + " ");
}
System.out.println();
}
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
Паттерн: шахматная доска
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if ((i + j) % 2 == 0) {
System.out.print("█ ");
} else {
System.out.print("░ ");
}
}
System.out.println();
}
█ ░ █ ░ █ ░ █ ░
░ █ ░ █ ░ █ ░ █
█ ░ █ ░ █ ░ █ ░
░ █ ░ █ ░ █ ░ █
█ ░ █ ░ █ ░ █ ░
░ █ ░ █ ░ █ ░ █
█ ░ █ ░ █ ░ █ ░
░ █ ░ █ ░ █ ░ █
Здесь используется if внутри вложенного цикла: если сумма индексов чётная — один символ, если нечётная — другой.
Паттерн: ромб
int n = 5;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n - i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print("*");
}
System.out.println();
}
for (int i = n - 1; i >= 1; i--) {
for (int j = 1; j <= n - i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print("*");
}
System.out.println();
}
*
***
*****
*******
*********
*******
*****
***
*
Здесь используется комбинация вложенных циклов и if: сначала печатаем пробелы для выравнивания, потом звёздочки.
Паттерн: цифровой треугольник
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + " ");
}
System.out.println();
}
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
Вложенные циклы с break и continue
break и continue работают только с внутренним циклом:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 5; j++) {
if (j == 3) {
break;
}
System.out.println("i=" + i + ", j=" + j);
}
System.out.println("--- конец строки " + i + " ---");
}
Вывод:
i=1, j=1
i=1, j=2
--- конец строки 1 ---
i=2, j=1
i=2, j=2
--- конец строки 2 ---
i=3, j=1
i=3, j=2
--- конец строки 3 ---
break прерывает только внутренний цикл. Внешний продолжает работу.
Метки с вложенными циклами
outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
break outer;
}
System.out.println("i=" + i + ", j=" + j);
}
}
Вывод:
i=1, j=1
i=1, j=2
i=1, j=3
i=2, j=1
break outer прерывает оба цикла.
Вложенные while и for
Вложенные циклы могут быть разных типов:
int i = 1;
while (i <= 3) {
for (int j = 1; j <= 4; j++) {
System.out.print(i * j + "\t");
}
System.out.println();
i++;
}
Вывод:
1 2 3 4
2 4 6 8
3 6 9 12
Внешний — while, внутренний — for. Всё работает!
Практика: таблица умножения от 1 до 10
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
System.out.printf("%4d", i * j);
}
System.out.println();
}
printf("%4d", ...) — форматированный вывод: число в поле шириной 4 символа. Это выравнивает столбцы.
Практика: транспонирование матрицы
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println("Исходная матрица:");
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + "\t");
}
System.out.println();
}
System.out.println("\nТранспонированная:");
for (int j = 0; j < matrix[0].length; j++) {
for (int i = 0; i < matrix.length; i++) {
System.out.print(matrix[i][j] + "\t");
}
System.out.println();
}
Вывод:
Исходная матрица:
1 2 3
4 5 6
Транспонированная:
1 4
2 5
3 6
Совет: при работе с вложенными циклами начинайте с маленького размера (2×2, 3×3), убедитесь, что код работает, а потом увеличивайте.
Практика: перемножение двух матриц
Умножение матриц — классическая задача с тройными вложенными циклами:
int[][] a = {
{1, 2},
{3, 4}
};
int[][] b = {
{5, 6},
{7, 8}
};
int[][] result = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
result[i][j] += a[i][k] * b[k][j];
}
}
}
System.out.println("Результат умножения:");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
System.out.print(result[i][j] + "\t");
}
System.out.println();
}
Вывод:
Результат умножения:
19 22
43 50
Практика: проверка симметричности матрицы
int[][] matrix = {
{1, 2, 3},
{2, 5, 6},
{3, 6, 9}
};
boolean symmetric = true;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] != matrix[j][i]) {
symmetric = false;
break;
}
}
if (!symmetric) break;
}
System.out.println("Матрица " + (symmetric ? "симметрична" : "не симметрична"));
Вывод:
Матрица симметрична
Практика: рисуем рамку
int width = 10;
int height = 5;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (i == 0 || i == height - 1 || j == 0 || j == width - 1) {
System.out.print("* ");
} else {
System.out.print(" ");
}
}
System.out.println();
}
Вывод:
* * * * * * * * * *
* *
* *
* *
* * * * * * * * * *
Практика: рисуем пустой ромб
int n = 5;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n - i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
if (j == 1 || j == 2 * i - 1) {
System.out.print("*");
} else {
System.out.print(" ");
}
}
System.out.println();
}
for (int i = n - 1; i >= 1; i--) {
for (int j = 1; j <= n - i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
if (j == 1 || j == 2 * i - 1) {
System.out.print("*");
} else {
System.out.print(" ");
}
}
System.out.println();
}
Вывод:
*
* *
* *
* *
* *
* *
* *
* *
*
Практика: таблица квадратов и кубов
System.out.println("Число\tКвадрат\tКуб");
System.out.println("─────────────────────");
for (int i = 1; i <= 10; i++) {
System.out.printf("%d\t%d\t%d%n", i, i * i, i * i * i);
}
Вывод:
Число Квадрат Куб
─────────────────────
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
Практика: генерация таблицы логарифмов
System.out.println("Число\tlog₂\tlog₁₀\tln");
System.out.println("─────────────────────────────");
for (int i = 1; i <= 10; i++) {
System.out.printf("%d\t%.3f\t%.3f\t%.3f%n",
i,
Math.log(i) / Math.log(2),
Math.log10(i),
Math.log(i));
}
Практика: создание двумерного массива с данными
int rows = 4;
int cols = 5;
int[][] data = new int[rows][cols];
int counter = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
data[i][j] = counter++;
}
}
System.out.println("Заполненная матрица:");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
System.out.printf("%3d", data[i][j]);
}
System.out.println();
}
Вывод:
Заполненная матрица:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Практика: рисуем пирамиду с числами
int n = 5;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n - i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print(j);
}
System.out.println();
}
Вывод:
1
123
12345
1234567
123456789
Практика: рисуем ёлочку
int height = 6;
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= height - i; j++) {
System.out.print(" ");
}
for (int j = 1; j <= 2 * i - 1; j++) {
System.out.print("*");
}
System.out.println();
}
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= height - 2; j++) {
System.out.print(" ");
}
System.out.println("|");
}
Вывод:
*
***
*****
*******
*********
***********
|
|
Совет: при создании паттернов начинайте с рисунка на бумаге. Определите закономерность: сколько пробелов, сколько символов, как зависит от номера строки. Затем перенесите это в код.
Итоги урока
- Вложенные циклы — это циклы внутри циклов
- На каждую итерацию внешнего цикла внутренний выполняется полностью
- Сложность: O(n × m), где n — внешний, m — внутренний
- Идеально подходят для таблиц, матриц и паттернов
breakиcontinueработают только с внутренним циклом (без метки)- Для прерыва внешнего цикла используйте метки:
break outer - Можно комбинировать разные типы циклов:
forвнутриwhileи наоборот - Комбинация
for + ifвнутри вложенных циклов позволяет создавать сложные паттерны - Паттерны создаются комбинацией пробелов и символов в правильных пропорциях
Следующий урок
В следующем уроке мы закрепим все знания о циклах на практических алгоритмах: поиск максимума/минимума, подсчёт элементов, проверка на простоту, Фибоначчи, факториал, разворот числа.
Урок 6: Циклы на практике →Тест по вложенным циклам
1 вопрос