再帰

関数が自分自身を呼び出して問題を解く手法で、木構造やフラクタル的な問題に適する

アルゴリズムプログラミング

再帰とは

再帰 (Recursion) は、関数が自分自身を呼び出して問題を解く手法である。問題を同じ構造の小さな部分問題に分割し、基底条件 (Base Case) で再帰を停止する。

基本構造

function factorial(n: number): number {
  if (n <= 1) return 1;          // 基底条件
  return n * factorial(n - 1);    // 再帰呼び出し
}
// factorial(4) → 4 * factorial(3) → 4 * 3 * factorial(2) → 4 * 3 * 2 * 1

再帰 vs ループ

// 再帰
function sum(arr: number[]): number {
  if (arr.length === 0) return 0;
  return arr[0] + sum(arr.slice(1));
}

// ループ (同じ結果)
function sum(arr: number[]): number {
  let total = 0;
  for (const n of arr) total += n;
  return total;
}
観点 再帰 ループ
可読性 木構造に自然 線形処理に自然
メモリ スタックを消費 一定
パフォーマンス オーバーヘッドあり 高速

木構造の走査

type TreeNode = { value: number; children: TreeNode[] };

function sumTree(node: TreeNode): number {
  let total = node.value;
  for (const child of node.children) {
    total += sumTree(child); // 再帰で子ノードを走査
  }
  return total;
}

ファイルシステムの走査

import { readdirSync, statSync } from 'fs';

function listFiles(dir: string): string[] {
  const entries = readdirSync(dir);
  return entries.flatMap(entry => {
    const path = `${dir}/${entry}`;
    return statSync(path).isDirectory() ? listFiles(path) : [path];
  });
}

スタックオーバーフロー

// ❌ 基底条件がない → 無限再帰
function bad(n: number): number { return bad(n - 1); }
// RangeError: Maximum call stack size exceeded

// ✅ 末尾再帰 (一部の環境で最適化)
function factorial(n: number, acc = 1): number {
  if (n <= 1) return acc;
  return factorial(n - 1, n * acc);
}

再帰が適するケース

ケース
木構造の走査 DOM、ファイルシステム、JSON
分割統治 マージソート、クイックソート
バックトラッキング 迷路、数独
フラクタル 再帰的な図形描画

再帰が不適なケース

ケース 代替
単純な繰り返し for/while ループ
深い再帰 (> 10,000) ループに変換
重複する部分問題 動的計画法 (メモ化)

理論と実装の両面から学ぶなら関連書籍が参考になる。

関連用語