JavaRush /Курсы /Java Collections /Рекурсия

Рекурсия

Java Collections
4 уровень , 1 лекция
Открыта

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

Рекурсия - 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 Уровень 47
28 октября 2024
Установите несколько зеркал, так чтобы они отражали друг друга. Заглянув в одно из них вы увидите рекурсию. Если серьезно: сама концепция рекурсии до безобразия проста, но вот ее практическое исполнение, далеко не всегда тривиально. Технически просто, а вот логически для нашего мозга иногда это выглядит не стандартно. Но есть ситуации когда использование рекурсии прямо очень логично и понятно... Тут главное ее с умом применять, а то можно простую задачу так усложнить рекурсией (хоть и сильно сократив код) что потом какой-нибудь почтенный программист разбирая ваш легаси-код 20-летней давности, преждевременно облысеет вырвав себе волосы от отчаяния. И еще оно очень сильно жрет ресурсы по причине изложенной в лекции. Так что употреблять стоит, но без фанатизма.
Long_byte Уровень 43
31 августа 2024
я понять не мог как работает рекурсия пока до меня не дошло что метод вызвавший внутри себя другого метода не завершает свою работу и текущий метод начинает свою работу там где он остановился
Anemon Уровень 13 Expert
5 марта 2025
Тоже..)
Ra Уровень 16 Student
30 июля 2023
Рекурсия курильщика

class RecursionDemo {
    static void recursion() {
        try {
            recursion();
        } finally {
            recursion();
        }
    }

    public static void main(String[] args) {
        RecursionDemo.recursion();
    }
}
Алексей С Уровень 33
3 июля 2023
Немного запутанные примеры на слайдах, но суть я уловил т.к я уже создавал рекурсию(хоть и сам не знал как это называется)
Кирилл Уровень 2
7 июня 2023
Странно конечно проходить рекурсию на 35 уровне... Мне кажется, уже каждый, пришедший сюда, как минимум изучил и несколько раз воспользовался рекурсией в задачах. А скорее - уже и прочувствовал, и набил руку.
Gans Electro Уровень 40
7 августа 2023
Было только пару раз. Вот люди же пишут что тоже использовали, но не знали как это называется или как работает
Василий Чи Уровень 51
15 января 2023

Person you;
boolean understood;
if (understood) return;
else tryingAgain(you);
Майкл Мэдсен Уровень 51
12 октября 2022
16 ноября 2022
Если какую-либо тему уже объяснил Алишев - лучше начать с его видео))
Сергей Смарт Уровень 51
29 августа 2022
Применял рекурсию уже в некоторых задачах, из последних это генератор паролей, если не проходил все проверки вызывал тот же метод
18 февраля 2023
Я применял в предыдущей задаче про вставку комментов перед тегом <second> в ХML. На случай если у тега были подтеги.
Igor Petrashevsky Уровень 47
22 августа 2022
4й квест, 40й уровень. самое время для рекурсий
LuneFox Уровень 41 Expert
17 января 2022
Если не до конца поняли, что такое рекурсия, прочитайте мой комментарий от 17 января 2022 года вот на этой странице.
FreyGreen Уровень 41
24 января 2022
Гений🤣🤣🤣
hidden #2595317 Уровень 45
24 февраля 2022
ты плохой
Роман Кончалов Уровень 28 Expert
30 марта 2022
Понимание рекурсии – условие выхода из рекурсии, гениально 👍
Aleksandr Gorohov Уровень 28
26 июля 2022
Пхаххаха!)))
ArturZ Уровень 42
2 августа 2022
Переходил по ссылкам два часа кряду и преисполнился
LuneFox Уровень 41 Expert
2 августа 2022
Вкладк оверфлоу?)
ArturZ Уровень 42
4 августа 2022
чтобы прийти в себя пришлось идти за всяким, случилось zakladkOverFlow
Oleg Khilko Уровень 51
9 августа 2022
На самом же деле это не совсем рекурсия. Скорее более подходящий пример рекурсии это фильм Нолана "Начало", где они погружались все глубже и глубже в сны и не могли выйти выше на уровень сна пока не завершат свой текущий уровень сна. Лучше посмотрите видео Наиля на эту тему и если Вы после него не поймете в чем отличие данных примеров - продолжайте щёлкать на сообщение LuneFox и читать мой коммент + смотреть ролик Наиля. Объяснение рекурсии
Igor Petrashevsky Уровень 47
22 августа 2022
это бесконечный цикл, а не рекурсия
LuneFox Уровень 41 Expert
25 августа 2022
Это рекурсия, если ты планируешь в конце узнать ответ и потом выйти переходами "назад" в браузере до того момента, с которого начал (размотать стек)
Lyokha Blagodatskikh Уровень 48
25 октября 2022
Сначала улыбнулся, мол забавно, а потом спустя секунд 5 до меня дошло ))))))))
Виктор Уровень 1
26 ноября 2022
а ты хорош! :)