Рекурсия

Открыта

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

Рекурсия - 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 – выводим имя текущего файла.

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

Комментарии (77)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Denis Odesskiy Full Stack Developer
28 октября 2024, 02:12
Установите несколько зеркал, так чтобы они отражали друг друга. Заглянув в одно из них вы увидите рекурсию. Если серьезно: сама концепция рекурсии до безобразия проста, но вот ее практическое исполнение, далеко не всегда тривиально. Технически просто, а вот логически для нашего мозга иногда это выглядит не стандартно. Но есть ситуации когда использование рекурсии прямо очень логично и понятно... Тут главное ее с умом применять, а то можно простую задачу так усложнить рекурсией (хоть и сильно сократив код) что потом какой-нибудь почтенный программист разбирая ваш легаси-код 20-летней давности, преждевременно облысеет вырвав себе волосы от отчаяния. И еще оно очень сильно жрет ресурсы по причине изложенной в лекции. Так что употреблять стоит, но без фанатизма.
Long_byte
Уровень 60
31 августа 2024, 19:53
я понять не мог как работает рекурсия пока до меня не дошло что метод вызвавший внутри себя другого метода не завершает свою работу и текущий метод начинает свою работу там где он остановился
Anemon
Уровень 13
Expert
5 марта 2025, 03:32
Тоже..)
Ra
Уровень 16
Student
30 июля 2023, 11:45
Рекурсия курильщика
class RecursionDemo {
    static void recursion() {
        try {
            recursion();
        } finally {
            recursion();
        }
    }

    public static void main(String[] args) {
        RecursionDemo.recursion();
    }
}
Алексей С
Уровень 33
3 июля 2023, 08:31
Немного запутанные примеры на слайдах, но суть я уловил т.к я уже создавал рекурсию(хоть и сам не знал как это называется)
Кирилл
Уровень 2
7 июня 2023, 13:18
Странно конечно проходить рекурсию на 35 уровне... Мне кажется, уже каждый, пришедший сюда, как минимум изучил и несколько раз воспользовался рекурсией в задачах. А скорее - уже и прочувствовал, и набил руку.
Gans Electro
Уровень 52
7 августа 2023, 04:29
Было только пару раз. Вот люди же пишут что тоже использовали, но не знали как это называется или как работает
Василий Чи
Уровень 1
15 января 2023, 22:24
Person you;
boolean understood;
if (understood) return;
else tryingAgain(you);
Майкл Мэдсен
Уровень 51
12 октября 2022, 11:59
16 ноября 2022, 16:57
Если какую-либо тему уже объяснил Алишев - лучше начать с его видео))
Сергей Смарт
Уровень 51
29 августа 2022, 11:06
Применял рекурсию уже в некоторых задачах, из последних это генератор паролей, если не проходил все проверки вызывал тот же метод
18 февраля 2023, 16:44
Я применял в предыдущей задаче про вставку комментов перед тегом <second> в ХML. На случай если у тега были подтеги.
Igor Petrashevsky
Уровень 47
22 августа 2022, 19:47
4й квест, 40й уровень. самое время для рекурсий
LuneFoxJava Developer в BIFITExpert
17 января 2022, 00:30
Если не до конца поняли, что такое рекурсия, прочитайте мой комментарий от 17 января 2022 года вот на этой странице.
FreyGreen
Уровень 41
24 января 2022, 20:03
Гений🤣🤣🤣
hidden #2595317
Уровень 45
24 февраля 2022, 10:19
ты плохой
Роман Кончалов Java Developer Expert
30 марта 2022, 10:19
Понимание рекурсии – условие выхода из рекурсии, гениально 👍
Aleksandr GorohovJava Developer в Globus IT
26 июля 2022, 12:40
Пхаххаха!)))
ArturZ
Уровень 42
2 августа 2022, 08:50
Переходил по ссылкам два часа кряду и преисполнился
LuneFoxJava Developer в BIFITExpert
2 августа 2022, 20:43
Вкладк оверфлоу?)
ArturZ
Уровень 42
4 августа 2022, 14:05
чтобы прийти в себя пришлось идти за всяким, случилось zakladkOverFlow
Oleg Khilko
Уровень 51
9 августа 2022, 13:20
На самом же деле это не совсем рекурсия. Скорее более подходящий пример рекурсии это фильм Нолана "Начало", где они погружались все глубже и глубже в сны и не могли выйти выше на уровень сна пока не завершат свой текущий уровень сна. Лучше посмотрите видео Наиля на эту тему и если Вы после него не поймете в чем отличие данных примеров - продолжайте щёлкать на сообщение LuneFox и читать мой коммент + смотреть ролик Наиля. Объяснение рекурсии
Igor Petrashevsky
Уровень 47
22 августа 2022, 19:47
это бесконечный цикл, а не рекурсия
LuneFoxJava Developer в BIFITExpert
25 августа 2022, 12:21
Это рекурсия, если ты планируешь в конце узнать ответ и потом выйти переходами "назад" в браузере до того момента, с которого начал (размотать стек)
Lyokha Blagodatskikh
Уровень 48
25 октября 2022, 06:07
Сначала улыбнулся, мол забавно, а потом спустя секунд 5 до меня дошло ))))))))
Виктор
Уровень 1
26 ноября 2022, 12:52
а ты хорош! :)