Как понять концепцию рекурсии в Java?

1

Я новичок в программировании на Java, и наш учитель научил нас концепции рекурсии, и я обнаружил, что это немного сложно. Все, что я понял, что это работает как цикл (например, факториал 4), но я все еще не совсем понимаю, почему он работает так. Могу ли я получить подробное объяснение по этой теме? Вот фрагмент кода и фотография, которую мой учитель объяснял.

package javaapplication1;

public class JavaApplication1 {

static int factorial(int n){
    int t;
    if(n == 0){
        return 1;
    } else {
        t = factorial(n - 1);
        return n * t;
    }
}
public static void main(String[] args) {
    System.out.println(factorial(5));
    }
}

На следующем изображении синий цвет представляет собой обмотку стека, а зеленый - раскручивание стека, и снова я не знаю, что такое намотка и размотка стека.

http://i.stack.imgur.com/pjqJy.png

  • 3
    См. Stackoverflow.com/questions/26041546
  • 1
    рекурсия - вызов функции, которая (в конце концов) вызывает себя снова. winding = вызов себя снова, разматывание = возврат из предыдущего рекурсивного вызова.
Показать ещё 2 комментария
Теги:
recursion
concept

5 ответов

1
Лучший ответ

Рекурсивная функция - это функция, которая вызывает себя до тех пор, пока не достигнет оператора return, который перестанет напоминать о себе. Возьмем ваш пример, функцию Factorial. Factorial - математическая функция, которая возвращает число, помноженное на себя - 1, умноженное на себя - 2,... умноженное на 1, например: factorial of 5 = 5! = 5x4x3x2x1 = 120. Он также равен самому себе, умноженному на его факториал -1, который равен: 5! = 5x4! Примите во внимание, что 0! = 1. Чтобы представить это в Java-коде, вам нужен цикл, который умножает числа, начинающиеся с 1, и идет до номера, который вы вычисляете его факториалом. Далее, объясняя ваш код, давайте вычислим Factorial (5): Factorial() возвращает целое число.

Первоначальный вызов из main(): 5! = 0, затем пропустите условие (n == 0); t = Факториал (5 -1) = Факториал (4);

Второй вызов из Factorial (4): 4! = 0, затем пропустите условие (n == 0); t = Factorial (4 -1) = Factorial (3);

Третий вызов из Factorial (3): 3! = 0, затем пропустите условие (n == 0); t = Факториал (3 -1) = Факториальный (2);

Четвертый вызов из Factorial (2): 2! = 0, затем пропустите условие (n == 0); t = Factorial (2 -1) = Factorial (1);

Пятый вызов из Factorial (1): 1! = 0, затем пропустите условие (n == 0); t = Factorial (1 -1) = Factorial (0);

Шестой вызов от Factorial (0): 0 == 0, затем возвращаемое значение 1;

Первый возврат, 1, пятый вызов (Factorial (1)): return n * t = return 1 * 1 = возвращаемое значение 1;

Второй возврат, 1, четвертый вызов (Factorial (2)): return n * t = return 2 * 1 = возвращаемое значение 2;

Третий возврат, 2, третий вызов (Factorial (3)): return n * t = return 3 * 2 = возвращаемое значение 6;

Второй возврат, 6, второй вызов (Factorial (4)): return n * t = return 4 * 6 = возвращаемое значение 24;

Второй возврат, 24, Первый вызов (Факториальный (5)): return n * t = return 5 * 24 = возвращаемое значение 120;

Второй возврат, 120, Начальный вызов (из main()): print (120);

Надеюсь, это поможет вам понять рекурсию.

  • 0
    Так есть ли у каждой рекурсивной функции подобная логика?
  • 0
    Это базовый пример, объясняющий процесс рекурсии, да. Рекурсия также является основной функцией при использовании двоичных деревьев, например, для поиска или для вставки значения на основе логики (зная, куда вставлять значение, слева или справа, проверяя каждый узел ...
1

Когда вызов выполняется другим способом, создается стоп-кадр, чтобы удерживать состояние текущего метода, и он помещается в стек. Это независимо от метода, вызывающего себя или другого метода.

Когда вызов возвращается, стек стека складывается из стека, состояние метода восстанавливается и выполнение продолжается в вызывающем методе.

Рекурсия - это когда метод (прямо или косвенно) вызывает себя. Общая форма рекурсивного метода:

  • Если параметр удовлетворяет условию завершения, возвращайте (как правило, результат)
  • Другие параметры настройки для следующей итерации и вызова

Код, который написал ваш учитель, имеет некоторые проблемы с стилем. Было бы яснее, если бы написано так:

static int factorial(int n) {
    if (n == 0) {
        return 1;
    } 
    return n * factorial(n - 1);
}

Искоренение ненужной переменной t и избыточного else (нет "else", когда возвращается "if" - есть просто продолжение выполнения)

Я бы написал это так, исключив, if вообще:

static int factorial(int n) {
    return n == 0 ? 1 : n * factorial(n - 1);
}
  • 0
    Поэтому, если у меня есть несколько условий, вместо использования if, else if {} ... else {}, я просто использую «:» и:?: После каждого условия, пока у меня не останется никаких других опций?
  • 0
    @ Frost, троичный оператор должен использоваться разумно: условия вложения обычно теряют читабельность.
0

Лично мне не нравится факторная проблема. Мне трудно понять, и я не думаю, что это объясняет рекурсию ясным образом. Поэтому давайте рассмотрим другой пример. Допустим, что мы хотим печатать цифры от 1 до 100. Это очень простая задача с циклом for и счетчиком, но это также можно сделать с рекурсией. Например:

public static void main(String[] args) {
    numbersAscending(1);
    numbersDescending(1);   
}
//Prints 1 2 3 ... 100
public void numbersAscending(int x){
    System.out.println(x);
    if(x < 100){
        numbersAscending(x+1);
    }
}
//Prints 100 99 98 ... 1
public void numbersDescending(int x){
    if(x < 100){
        numbersDescending(x+1);
    }
    System.out.println(x);
}

Когда вызывается функция, этот вызов идет поверх стека. Подумайте об этом, как стек карт. У каждого из них есть номер (1-100). Когда функция вызывает себя, в стек добавляется новая карта. Когда функция заканчивается, она снимается со стека.

Итак, для примера выше, каждый раз, когда вызывается numberAscending, он выдает текущее значение для x, прежде чем снова вызвать эту функцию. Это приводит к тому, что числа печатаются в порядке от 1 до 100. Как только 100 достигнуто, он перестает звонить себе и выталкивает каждую функцию из стека.

С другой стороны, каждый раз, когда вызывается numberDescending, он вызывает себя снова, прежде чем печатать номер. Таким образом, x не запускает печать до тех пор, пока не достигнет 100. Затем он возвращается обратно в стек, печатая каждый номер, когда он возвращается к основному методу.

0

Когда кто-то знает, что задача может быть разбита на подобные меньшие задачи, мы используем рекурсию или вызов того же метода (пока не встретим какое-то условие). Recursion не только помогает в выполнении задачи без необходимости определять или вызывать другой метод, но также помогает визуализировать шаблон, с помощью которого задача выполняется

  • 0
    Как мне выяснить, в каких случаях я использую рекурсивную функцию и создаю ее?
  • 0
    Если вы видите, что ваша функция может быть разбита на более мелкие идентичные функции с уменьшающейся сложностью. Например, чтобы найти факториал n, вам нужно (n-1)! * N и (n-1)! можно рассчитать с помощью (n-2)! и так далее. Итак, вы обнаружите, что определить одну функцию и вызвать ее с меньшими параметрами проще, поэтому вы должны использовать рекурсию
-2

Я не уверен, объясняет ли он, но если у вас есть класс precalculus, тогда вы должны знать, что факториал может быть определен в двух waqys.

п! = 1 * 2 *... * п

мы определяем

1! = 1

а также

п! = п * (п-1)!

Постарайтесь убедиться, что эти определения эквивалентны. Возьмите, скажем так, 5!

согласно второму определению

5! = 5 * 4!

но 4! = 4 * 3! так что 5! = 5 * 4 * 3!

но 3! = 3 * 2! поэтому 5! = 5 * 4 * 3 * 2!

и так далее. Продолжайте делать это, пока не нажмете 1 !. Но 1! = 1, поэтому остановитесь.

Рекурсия в программировании - это одно и то же.

TomW

Ещё вопросы

Сообщество Overcoder
Наверх
Меню