Как мыслить рекурсивно?

чтобы понять передовые концепции алгоритмов, такие как жадные методы и динамическое программирование, сначала нужно хорошо разбираться в рекурсии.

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

пожалуйста, помогите, ответив ниже вопросы:

1) Может ли любой итерационный метод быть заменен рекурсией и наоборот?

например, как рекурсивно печатать элементы в массиве размера n?

for i 0 to n
 Print a[i]

2) как решить проблему рекурсивно? Какие шаги? Есть ли какие-либо советы, чтобы определить, что проблемы могут быть решены рекурсивно?

например: если меня попросят распечатать все подстроки строки

INPUT: CAT
OUTPUT: CAT,CA,A,AT,T

Я могу быстро придумать итеративный способ.С помощью две петли могут решить проблему.

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

Если ответ на мой первый вопрос да, использование двух рекурсий вместо итерации может быть решением моей проблемы?

3) Может ли кто-нибудь предложить мне некоторые материалы/ресурсы, чтобы полностью понять концепцию рекурсии?

5 ответов


  1. да, в основном. В целом рекурсия выполняется ради программиста, а не компьютера. Есть итерационные методы, которые в некоторых случаях могут работать быстрее, чем рекурсивные, но итерационный метод может принимать 300 строк кода и рекурсивные 3. Есть также случаи, когда легко понять, как программировать что-то рекурсивно, но очень сложно писать итеративно и наоборот.

  2. обычно рекурсивное решение должен быть хотя с точки зрения функции. Если мы используем что-то вроде C++, мы можем использовать решение, касающееся ссылок на строки и вещей, медленно регулируя строки, передаваемые в качестве параметров. Однако точка в конце "двух рекурсий" ошибочна. Принцип здесь заключается в том, что вместо двух итераций, мы можем сделать один рекурсивный подход.

  3. http://introcs.cs.princeton.edu/java/23recursion/ этот сайт (высоко в поиске google) преподает много математической теории рекурсии и включает FAQ, который может дать вам более удовлетворительный ответ на номер один.


существует способ мышления о рекурсии, который делает его таким же простым, как итерация.

в итерации у нас есть цикл. Подумайте об этом, как имеющие 4 части:

  1. решение продолжить или остановить, основанное на некоторых "контролирующих" данных, оцениваемых как логическое условие.

  2. тело, где работа делается. Иногда тело сочетается со следующей частью.

  3. способ изменения "контролирующих" данных. Часто меняю счетчик.

  4. способ вызова конструкции (в данном случае цикла) снова. В языках C-стиля это обеспечивается синтаксисом for, while или do.

в рекурсии у нас есть функция (иногда несколько). Они имеют те же 4 части:

  1. решение продолжить или остановить, основанное на некоторых "контролирующих" данных, оцениваемых как логическое условие. Управляющие данные обычно передаются функции как параметр(ы).

  2. тело, где работа делается. Иногда тело сочетается со следующей частью.

  3. способ изменения "контролирующих" данных. Часто меняя счетчик.

  4. способ вызова конструкции (в данном случае функции) снова - это означает вызов функции (и не забудьте передать измененные "управляющие" данные.

неудивительно, что эти два конструкции имеют одинаковые части, так как они эквивалентны.


давайте возьмем простую задачу. Печать чисел от 1 до 10. Я буду использовать Python2.7 здесь.

for i in range(1,11):
    print i

теперь попробуем сделать то же самое, используя рекурсию.

>>> def print_me(n):
    if n > 0:
        print_me(n - 1)
        print n
    else:
        return

>>> print_me(10)
1
2
3
4
5
6
7
8
9
10

Итак, как мы думаем об этом ?

  • Шаг 1: подумайте о моих границах. Мне нужно два . 1 и 10. Следующий.
  • Шаг 2: заявление, печать. Это наш мотив. Печатать номера. И мы хотим от 1 до 10. Поэтому мне нужно распечатать 1 первый.
  • Шаг 3: мы начинаем с написания функции, которая выполняет работу печати номер передается в качестве аргумента. Давайте подумаем только о главном задача.

    def print_me(n): print n

  • Шаг 4: Я хочу, чтобы функция возвращалась, если n

    def print_me(n): если n > 0: печать Н еще: возвращение

  • Шаг 5: Теперь Я хотите передать числа от 1 до 10 в эту функцию, но мы не хотим цикл от 1 до 10 , а затем передать его нашей функции. Мы хочу, чтобы это было сделано рекурсивно.

что такое рекурсия? Проще говоря, повторное применение рекурсивной процедуры или определения.

поэтому, чтобы сделать его рекурсивным, нам нужно вызвать саму функцию. И мы хотим пройти наш диапазон от 1 до 10.

def print_me(n):
    if n > 0:
        print_me(n - 1)
        print n
    else:
        return

обобщение: Все рекурсивные вызовы должны подчиняться 3 важным правилам:

  1. рекурсивный алгоритм должен иметь базовый вариант.
  2. рекурсивный алгоритм, должен изменить свое состояние и двигаться к базе случай.
  3. рекурсивный алгоритм должен вызывать себя рекурсивно.

источник : interactivepython

другая программа в Javascript для поиска факториала:

function factorial(n){
    if (n == 1)
        {
        return 1;
        }
    else {
        return n * factorial(n-1);
        }
}

@Test
public void testStrings() {
   TreeSet<String> finalTree = getSubStringsOf("STACK");
    for(String subString : finalTree){
        System.out.println(subString);
    }
}

public TreeSet<String> getSubStringsOf(String stringIn) {
    TreeSet<String> stringOut = new TreeSet<String>();
    if (stringIn.length() == 1) {
        stringOut.add(stringIn);
        return stringOut;
    } else {
        for (int i = 1; i < stringIn.length() ; i++) {
            String stringBefore = stringIn.substring(0, i);
            String stringAfter = stringIn.substring(i);
            stringOut.add(stringBefore);
            stringOut.add(stringAfter);
            stringOut.addAll(getSubStringsOf(stringBefore));
            stringOut.addAll(getSubStringsOf(stringAfter));
        }
        return stringOut;
    }
}

Я не знаю, нужно ли вам объяснение. Разделить строку на две каждый раз, когда это возможно. Таким образом,Cat разделяется на CA,T и C, AT, вы добавляете их в свой список подстрок, а затем ищете каждую подстроку этих подстрок. Если строка состоит из одного символа, вы добавляете его в дерево.

EDIT: это выход для стека:

A AC ACK C CK K S ST STA STAC T TA TAC TACK

EDIT again: как вы можете видеть, каждый раз, когда вы запускаете подстроку метода, вы будете использовать он дважды внутри него, за исключением одной символьной строки. Таким образом, сложность равна O (n2). Для "стека" длина программы составляет 0,200 мс, для "СТЕКСТАКСТАКА" (3-кратный стек) - 2секунды, для "СТЕКСТАКСТАКСТАКСТАКА" теоретически в 2^10 раз больше, таким образом, 2000 секунд.


вот мой простой тест на Python:

def p(l, index):
    if index == len(l):
        return
    else:
        print l[index]
        index = index + 1
        p(l, index)

и звонок:

p("123456", 0)