Итеративное дерево

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

Что, если таковые имеются, являются преимуществами итеративного или рекурсивного обхода? В каких ситуациях я могу использовать один, а не другой?

Ответы

Ответ 1

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

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

Некоторые быстрые Python, чтобы проиллюстрировать разницу:

#A tree is a tuple of an int and a tree. 
t = (1, (2,(4, (6), (7, (9)) )), (3, (5, (8)) ))
def bfs(t):
    to_visit = [t]
    while len(to_visit) > 0:
        c = to_visit[0]
        if type(c) is int:
            print c
        else: 
            print c[0]
            to_visit.append(c[1])
            if len(c) > 2: to_visit.append(c[2])
        to_visit = to_visit[1:]

def dfs(t):
    if type(t) is int:
        print t
        return 
    print t[0]
    dfs(t[1])
    if len(t) > 2: dfs(t[2]) 

bfs(t)
dfs(t)

Ответ 2

Если у вас есть фиксированный объем памяти, выделенный для стека, как вы часто это делаете (это особенно проблема во многих конфигурациях Java JVM), рекурсия может не сработать, если у вас есть глубокое дерево (или если глубина рекурсии в любом другом сценарии); это вызовет переполнение стека. Итеративный подход, толкание узлов для посещения очереди (для обхода типа BFS) или стека (для обхода, подобного DFS) имеет лучшие свойства памяти несколькими способами, поэтому, если это имеет значение, используйте итеративный подход.

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

Ответ 3

Это зависит от того, хотите ли вы совершать обход Глубины или Первый шаг. Глубина первой проще всего реализовать с помощью рекурсии. Сначала с помощью Breadth вам нужно сохранить очередь узлов для расширения в будущем.

Ответ 4

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

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

Ответ 5

Перейдите с рекурсивным, потому что вы действительно можете получить ошибку, и в конце концов это stackoverflow.com.