Java | 再帰的なアルゴリズムの実例集

Java Java
スポンサーリンク

Javaで学ぶ再帰アルゴリズム(前半)

Javaの再帰は、初心者にとって「少し難しそう」に見えるテーマですが、実は仕組みが分かればとてもシンプルです。 この前半では、再帰の基本構造、Javaでの書き方、そして代表的な例題を丁寧にかみ砕いて解説します。 後半では、ツリー構造や実務的な再帰、セキュリティ観点まで踏み込んでいきます。

再帰とは何か(Javaでの基本構造)

再帰とは「メソッドが自分自身を呼び出すこと」です。 Javaでは次のような形が基本になります。

void method() {
    // ベースケース(終了条件)
    if (/* 終わりの条件 */) {
        return;
    }

    // 問題を少しだけ小さくして自分自身を呼び出す
    method();
}
Java

深掘り:再帰の2大原則

再帰は次の2つがそろって初めて成立します。

ベースケース(終了条件) どこで再帰を止めるかを必ず明確にする。 これがないと無限ループになり、Javaは StackOverflowError を投げます。

問題を小さくするステップ 毎回「少しだけ簡単な問題」にして自分自身を呼び出す。 これにより、必ずベースケースに近づいていきます。

この2つを理解することが、再帰の本質です。

Javaで最も基本的な再帰:カウントダウン

まずは、初心者向けに「カウントダウン」を再帰で書いてみます。

public static void countdown(int n) {
    System.out.println(n);

    if (n == 0) {
        return; // ベースケース
    }

    countdown(n - 1); // 問題を小さくして再帰
}

public static void main(String[] args) {
    countdown(5);
}
Java

動きのイメージ

countdown(5) → 5 を表示して countdown(4) → 4 を表示して countdown(3) → … → countdown(0) で終了条件に到達し、再帰が止まる

再帰は「深く潜っていき、最後に戻りながら処理が終わる」という流れを持っています。

階乗(n!)を再帰で書く

再帰の代表例として必ず登場するのが「階乗」です。

public static int factorial(int n) {
    if (n == 0) {
        return 1; // ベースケース
    }

    return n * factorial(n - 1); // 再帰ステップ
}

public static void main(String[] args) {
    System.out.println(factorial(5)); // 120
}
Java

深掘り:数学の式と再帰の一致

階乗は数学的に次のように定義されます。

  • 0! = 1
  • n! = n × (n – 1)!

この「自分自身を使って定義する」形が、そのままJavaの再帰に対応しています。 再帰は数学的な定義をコードに落とし込むのに非常に向いています。

フィボナッチ数列(ただし注意付き)

フィボナッチ数列も再帰の代表例です。

public static int fib(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;

    return fib(n - 1) + fib(n - 2);
}
Java

深掘り:なぜ「注意付き」なのか

この再帰は美しいですが、計算量が爆発します。 同じ値を何度も計算するため、fib(40) などは非常に遅くなります。

後半では、この問題を解決する「メモ化」や「動的計画法」を扱います。

再帰の裏側:コールスタック

Javaはメソッドを呼び出すたびに「コールスタック」に情報を積み上げます。 再帰はこのスタックを深く使うため、終了条件がないとスタックが溢れます。

StackOverflowError

次のようなコードは危険です。

public static void bad(int n) {
    bad(n + 1); // 終了条件がない
}
Java

実行すると StackOverflowError が発生します。

深掘り:安全な再帰を書くためのポイント

  • 必ず終了条件を書く
  • 終了条件に確実に近づくようにする
  • 深さが大きくなりすぎる可能性がある場合はループに切り替える

再帰とループの違い

再帰は「自分自身を呼び出す」 ループは「同じ処理を繰り返す」

どちらも繰り返し処理ですが、得意分野が違います。

再帰が向いている場面

  • ツリー構造
  • ネストが深いデータ
  • 数学的な再帰定義
  • 問題を自然に分割できる場合

ループが向いている場面

  • 単純な繰り返し
  • 深さが大きくなる可能性がある処理
  • パフォーマンスが重要な場面

前半のまとめ

前半では、Javaの再帰の基礎と代表的な例を扱いました。

  • 再帰は「自分自身を呼び出す」
  • ベースケースと問題の縮小が必須
  • 階乗やフィボナッチは再帰の典型例
  • コールスタックの仕組みを理解すると再帰が分かりやすい
  • 再帰とループは使い分けが重要

後半では、より実務的な再帰の使い方として

  • ツリー構造の走査
  • ファイル探索
  • JSONやDOMの再帰処理
  • セキュリティ観点での安全な再帰
  • メモ化による高速化

などを深掘りしていきます。

タイトルとURLをコピーしました