ワンクリックで
algo-recursion
Recursion — base case, recursive case, recursion-depth limits, tail vs non-tail, when to convert to iteration.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Recursion — base case, recursive case, recursion-depth limits, tail vs non-tail, when to convert to iteration.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | algo-recursion |
| description | Recursion — base case, recursive case, recursion-depth limits, tail vs non-tail, when to convert to iteration. |
| when-to-use | Tree/graph traversal, divide-and-conquer (mergesort, quicksort), backtracking, recursive structure naturally — JSON walks, regex compilation. |
A recursive function solves a problem by reducing it to a smaller instance of the same problem. Get the base case right and the recursive case becomes a one-line transformation.
T(n) = T(n/2) + O(1) →
O(log n). T(n) = T(n-1) + O(1) → O(n). T(n) = 2 T(n-1)
→ O(2^n). Use the Master Theorem for divide-and-conquer.O(depth) for the call stack. For a balanced binary
tree of n nodes, depth is O(log n). For a degenerate (linked-
list-shaped) tree, depth is O(n).class Node:
def __init__(self, val: int, left=None, right=None):
self.val, self.left, self.right = val, left, right
def in_order(root: Node | None, out: list[int]) -> None:
if root is None: return # base case
in_order(root.left, out) # recurse left
out.append(root.val) # visit
in_order(root.right, out) # recurse right
The None check is the base case. The recursive case trusts
that in_order(root.left, out) correctly populates out for the
left subtree.
def mergesort(arr: list[int]) -> list[int]:
if len(arr) <= 1: return arr[:] # base case
mid = len(arr) // 2
left = mergesort(arr[:mid])
right = mergesort(arr[mid:])
return _merge(left, right)
def _merge(a: list[int], b: list[int]) -> list[int]:
out: list[int] = []
i = j = 0
while i < len(a) and j < len(b):
if a[i] <= b[j]: out.append(a[i]); i += 1
else: out.append(b[j]); j += 1
out.extend(a[i:]); out.extend(b[j:])
return out
The recursive case is trivial because _merge correctly merges
two sorted lists — encapsulation lets the recursion stay clean.
function solveNQueens(n) {
const cols = new Set(), d1 = new Set(), d2 = new Set();
const out = [];
const cur = [];
function place(row) {
if (row === n) { out.push([...cur]); return; }
for (let c = 0; c < n; c++) {
if (cols.has(c) || d1.has(row - c) || d2.has(row + c)) continue;
cols.add(c); d1.add(row - c); d2.add(row + c); cur.push(c);
place(row + 1);
cols.delete(c); d1.delete(row - c); d2.delete(row + c); cur.pop();
}
}
place(0);
return out;
}
The place(row + 1) recursion makes the choice; the cleanup
afterward (delete, pop) restores state for the next sibling.
That symmetry is the backtracking discipline.
if n == 0: return f(n) — call yourself again.sys.setrecursionlimit(10**6) early, or rewrite iteratively.[...cur]).@cache or convert to iteration.n == 0 and n == 1.When recursion runs into the depth limit, the conversion path is
usually: stack-of-frames → explicit [(state, kind)] list with
kind distinguishing pre- and post-order visits. The structure
maps 1:1 to the recursive function.
Breadth-first search — shortest path on unweighted graphs, level-order traversal, and the `visited` discipline that prevents O(2^n) blowups.
Binary search invariants, half-open intervals, and the `lo<hi` template that beats off-by-one bugs.
Depth-first search — recursive vs iterative, recursion-depth gotchas, three-color cycle detection, topological sort.
Dynamic programming — state design, memoization vs tabulation, dimension-reduction, and when DP is the wrong tool.
Pick the right traversal — BFS for unweighted shortest path, Dijkstra for weighted, A* for goal-directed, 0/1-BFS for binary weights.
Greedy algorithms — exchange-argument proofs, when greedy beats DP, classic patterns (interval scheduling, Huffman, scheduling).