Открыть сервис

Древовидная рекурсия

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

Определение и принцип работы

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

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

Примеры классических задач

Числа Фибоначчи

Наиболее известный пример древовидной рекурсии — наивное рекурсивное вычисление чисел Фибоначчи. Функция fib(n) определяется как:

При вычислении fib(5) функция вызывает fib(4) и fib(3). Каждый из этих вызовов порождает свои подвызовы, образуя дерево. Количество вызовов растёт экспоненциально (по закону, близкому к O(2^n)), хотя фактическое число вызовов для fib(n) равно 2*fib(n+1) - 1. Например, для вычисления fib(30) требуется более 2,6 миллионов вызовов, в то время как итеративный алгоритм или рекурсия с мемоизацией выполняют всего 31 операцию.

Вычисление биномиальных коэффициентов

Биномиальный коэффициент C(n, k) может быть вычислен рекурсивно с использованием свойства Паскаля:

Эта рекурсия также является древовидной, так как каждый вызов (кроме базовых) порождает два новых. Без оптимизации она приводит к экспоненциальному росту числа вызовов.

Задача о размене монет

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

Сравнение с другими типами рекурсии

Тип рекурсииКоличество рекурсивных вызовов на шагПримерВычислительная сложность
Линейная1Факториал, обход спискаO(n)
Хвостовая1 (вызов в конце)Факториал с аккумуляторомO(n)
Древовидная2 и болееЧисла Фибоначчи (наивно)O(2^n) (часто)
ВзаимнаяВызовы разных функцийАлгоритм пар-нечетЗависит от задачи

Преимущества и недостатки

Преимущества

Недостатки

Оптимизация: мемоизация

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

Мемоизация превращает древовидную рекурсию в рекурсию с запоминанием (top-down dynamic programming). Для чисел Фибоначчи это снижает сложность с O(2^n) до O(n). В общем случае, если количество уникальных подзадач полиномиально, мемоизация делает алгоритм полиномиальным.

Применение в информатике

Несмотря на потенциальную неэффективность, древовидная рекурсия широко используется в тех областях, где её применение естественно и где мемоизация или иные оптимизации решают проблему производительности:

Интересные факты

Источники

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →