Рекурсия

Модуль 2. Java Core
10 уровень , 0 лекция
Открыта

— Привет, Амиго. Сегодня Билаабо расскажет тебе, что такое рекурсия.

Рекурсия - 1

Как ты знаешь, в Java одни методы вызывают другие методы. При этом, при вызове метода, в него передаются конкретные значения аргументов, а во время его работы локальные переменные метода принимают некоторые значения.

— Ага.

— И как ты знаешь, внутренние переменные разных методов независимы друг от друга.

— Ага.

— Так вот, представь ситуацию, когда метод может вызвать сам себя. Именно она называется рекурсией. Пример:

Пример
public static void main(String[] args)
{
 countDown(10);
}

public static void countDown(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown(x - 1);
 }
}
Вывод на экран:
10
9
8
7
6
5
4
3
2
1
Boom!

— Честно говоря, вижу, что метод в коде сам себя вызывает, но не понимаю, что именно при этом происходит.

— Да примерно то же, что и при вызове другого метода.

— Нет, я спрашиваю, что происходит с переменными? С их значениями? И как мы выходим из метода? Или мы выходим из всех сразу?

— Господи. Да все гораздо проще. Представь что метод, который вызывает сам себя, размножили много раз. Тогда будет аналогичная ситуация:

Рекурсивный вызов метода Что происходит «на самом деле»
public static void main(String[] args)
{
 countDown(3);
}

public static void countDown(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown(x - 1);
 }
}
public static void main(String[] args)
{
 countDown1(3);
}

public static void countDown1(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown2(x - 1);
 }
}
public static void countDown2(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown3(x - 1);
 }
}
public static void countDown3(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown4(x - 1);
 }
}

public static void countDown4(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  countDown5(x - 1);
 }
}
Вывод на экран: Вывод на экран:
3
2
1
Boom!
3
2
1
Boom!

Т.е. каждый раз при вызове метода (даже самого себя) создаются новые переменные, которые хранят данные для этого метода. Никаких общих переменных нет.

При каждом вызове в памяти появляется еще одна копия аргументов метода, но уже с новыми значениями. При возвращении в старый метод, там используются его переменные. Т.е. при рекурсии фактически мы вызываем другой метод, но с таким же кодом как наш!

— Ясно. А как работает выход из этих методов? Можно пример?

— Ладно. Один пример стоит тысячи слов. Вот тебе пример:

Рекурсивный вызов метода Что происходит «на самом деле»
public static void main(String[] args)
{
 print(3);
}

public static void print(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  print(x - 1);
  System.out.println(x);
 }
}
public static void main(String[] args)
{
 print1(3);
}

public static void print1(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  print2(x - 1);
  System.out.println(x);
 }
}

public static void print2(int x)
{
 if (x <=0)
  System.out.println("Boom!");
 else
 {
  System.out.println(x);
  print3(x - 1);
  System.out.println(x);
 }
}

…
Вывод на экран: Вывод на экран:
3
2
1
Boom!
1
2
3
3
2
1
Boom!
1
2
3

— Ок. Вроде понял. А зачем нужна рекурсия?

— Есть очень много задач, которые разбиваются на отдельные подзадачи, идентичные первоначальной задаче. Например, надо обойти все элементы XML-дерева. У каждого элемента может быть несколько дочерних элементов, а у них — свои.

Или тебе нужно вывести список файлов, которые есть в директории и все ее поддиректориях. Тогда ты пишешь метод, который выводит файлы текущей директории, а потом для получения файлов всех поддиректорий вызываешь его же, но с другим параметром – поддиректорией.

Пример:

Вывод всех файлов на экран из директории и её поддиректорий
public static void main(String[] args)
{
 printAllFiles(new File("c:/windows/"));
}

public static void printAllFiles(File dir)
{
 for (File file : dir.listFiles())
 {
  if (file.isDirectory())
   printAllFiles(file);
  else
   System.out.println(file.getAbsolutePath()); 
 }
}

Строка 8 – получаем список всех файлов (и директорий) в директории dir.

Строки 10-11 – если файл на самом деле директория, то для вывода ее файлов опять вызываем printAllFiles, но уже с другим параметром – поддиректорией.

Строка 13 – выводим имя текущего файла.

— Ок. Вроде понял. Спасибо, Билаабо.

Комментарии (4)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #3613348 Уровень 40
25 декабря 2025
Рекурсия в Java (и программировании в целом) – это техника, когда функция вызывает саму себя для решения более мелкой версии той же задачи. Вот ключевые аспекты рекурсии: Базовый случай (Base Case): Условие, при котором функция перестает вызывать саму себя и возвращает конкретное значение. Без базового случая рекурсивная функция будет вызывать себя бесконечно, что приведет к переполнению стека (StackOverflowError). Базовый случай – это точка остановки рекурсии. Рекурсивный вызов (Recursive Call): Внутри функции происходит вызов самой себя, но с измененными аргументами. Эти измененные аргументы должны приближать проблему к базовому случаю. Как это работает: Когда функция вызывается рекурсивно, в памяти (в стеке вызовов) создается новый "фрейм" для этой функции, содержащий ее локальные переменные и параметры. Этот новый фрейм "ложится" поверх предыдущего фрейма. Когда базовый случай достигнут, рекурсивный вызов прекращается, и функция возвращает значение. Возвращенное значение передается в предыдущий фрейм, который продолжает вычисления. Этот процесс продолжается, пока все фреймы не будут обработаны и не будет возвращено итоговое значение.
Константин Уровень 81
8 сентября 2024
Я один читаю Бильбао вместо Билаабо?
12 октября 2023
Билаабо, что за токсичный тон!? "Господи. Да все гораздо проще."
Антон Уровень 108 Expert
19 мая 2022
Судя по размеру лекции, не похоже на Билаабо. Скорее уж Элли)