Які задачі розв'язує рекурсія
Рекурсію часто застосовують для задач, де велику задачу можна природно розділити на підзадачі того самого типу. Нижче найтиповіші класи таких задач із прикладами на JavaScript.
Теорія
TL;DR
- Математичні обчислення: факторіал, числа Фібоначчі, піднесення до степеня.
- Обхід структур даних: дерева (DOM, файлова система), графи, DFS.
- Робота з масивами: сума, пошук, фільтрація через «голова плюс хвіст».
- Алгоритми «розділяй і володарюй»: QuickSort, MergeSort, бінарний пошук, Ханойська вежа.
- Обробка вкладених структур: flatten масиву, обхід довільно вкладених об'єктів.
- Логічні та комбінаторні задачі: перестановки, шлях у лабіринті, рюкзак.
Швидкий приклад
// Піднесення до степеня через поділ задачі
function pow(x, n) {
if (n === 0) return 1; // базовий випадок
return x * pow(x, n - 1); // рекурсивний крок
}
console.log(pow(2, 10)); // 1024Математичні обчислення
Класика, бо формула вже задана рекурентно.
-
Факторіал числа:
n! = n * (n-1)! -
Числа Фібоначчі:
F(n) = F(n-1) + F(n-2) -
Піднесення до степеня через поділ задачі:
javascriptfunction pow(x, n) { if (n === 0) return 1; return x * pow(x, n - 1); }
Рекурсивний код тут майже дослівно повторює математичний запис, тому його легко перевірити очима.
Обхід структур даних
Дерево складається з піддерев, тому обхід дерева це обхід кожного піддерева.
-
Дерева (наприклад, DOM, файлова система):
javascriptfunction traverse(node) { console.log(node.value); node.children.forEach(traverse); } -
Графи: пошук у глибину (DFS) записується рекурсивно майже без зусиль; пошук у ширину (BFS) зазвичай реалізують через чергу, бо він іде рівнями, а не вглиб.
Для графів обов'язково запам'ятовуйте відвідані вузли у Set, інакше цикл у даних зациклить обхід.
Масиви та вкладені структури
Масив зручно розглядати як «перший елемент плюс решта масиву».
-
Сума елементів, пошук, фільтрація:
javascriptfunction sum(arr) { if (arr.length === 0) return 0; return arr[0] + sum(arr.slice(1)); } -
Розгортання (flatten) масиву:
javascriptfunction flatten(arr) { return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val), []); }
Саме вкладені структури, де глибина наперед невідома, є найсильнішим аргументом на користь рекурсії: циклом довелося б вести власний стек.
Алгоритми «розділяй і володарюй»
Задача ділиться на кілька менших, кожна розв'язується тим самим алгоритмом, потім результати збираються докупи.
- Швидке сортування (QuickSort)
- Сортування злиттям (MergeSort)
- Бінарний пошук
- Алгоритм Ханойської вежі
function binarySearch(arr, target, lo = 0, hi = arr.length - 1) {
if (lo > hi) return -1; // базовий випадок: нічого не лишилося
const mid = (lo + hi) >> 1;
if (arr[mid] === target) return mid;
return arr[mid] < target
? binarySearch(arr, target, mid + 1, hi)
: binarySearch(arr, target, lo, mid - 1);
}Логічні та комбінаторні задачі
- Перебір усіх комбінацій і перестановок;
- пошук шляху в лабіринті;
- розв'язання «зважених» задач типу рюкзака (knapsack).
function permutations(items) {
if (items.length <= 1) return [items]; // базовий випадок
return items.flatMap((item, i) => {
const rest = [...items.slice(0, i), ...items.slice(i + 1)];
return permutations(rest).map((p) => [item, ...p]);
});
}
console.log(permutations(['a', 'b', 'c']).length); // 6Тут рекурсія природно описує перебір з поверненням (backtracking): зробили вибір, пішли глибше, повернулися і спробували наступний.
Типові помилки
- Рекурсія там, де достатньо циклу. Лінійний прохід масивом циклом простіший і не має обмеження глибини стека.
arr.slice(1)на великому масиві. Кожен виклик копіює хвіст, тож замістьO(n)виходитьO(n^2)за часом і пам'яттю; краще передавати індекс.- Наївний
fibбез мемоізації.fib(n - 1) + fib(n - 2)дає експоненційну кількість викликів; кеш або цикл роблять його лінійним. - Обхід графа без множини відвіданих вузлів. Цикл у даних перетворює обхід на нескінченний.
- Глибока рекурсія по великих даних. Обхід мільйона елементів впаде з
RangeError: Maximum call stack size exceeded, навіть коли логіка правильна. - Забутий базовий випадок у комбінаторних задачах. Найчастіше це
items.length <= 1або порожня гілка дерева.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.