Recursion solves a problem by calling the same operation on a smaller state. Correctness requires a base case, progress toward it on every branch, and a way to combine returned results.
The base case returns without another recursive call. A progress measure such as remaining length, tree depth, or unvisited nodes must decrease toward that case. Validate input that could violate the measure, including cycles in structures assumed to be trees.
Each call stores parameters, locals, and a return location. Calls descend until a base case, then unwind in reverse order. Trace small input by writing one row per call and a second row per return; this separates downward work from result combination.
Trees, nested syntax, directory structures, and divide-and-conquer ranges naturally contain smaller instances of the same shape. Graph traversal additionally needs a visited set, or cycles can recurse forever.
Count both the number of calls and maximum active depth. A traversal may be O(n) time but O(h) stack space. For deep or untrusted input, replace implicit recursion with an explicit stack that stores each pending node and any post-child state. Memoization reduces repeated overlapping subproblems but does not repair a missing base case.
function sumTree(node) {
if (node === null) return 0;
return node.value + sumTree(node.left) + sumTree(node.right);
}
function visit(graph, start, seen = new Set()) {
if (seen.has(start)) return;
seen.add(start);
for (const next of graph.get(start) ?? []) visit(graph, next, seen);
}
Avoid uncontrolled depth, repeated subproblems without caching, and cases where an iterative loop or explicit stack is clearer and safer.
Practice, interview questions, and compiler links for Recursion in Data Structures.
Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.
Explore 500+ free tutorials across 20+ languages and frameworks.
Fresh tutorials, interview guides, and coding practice in your inbox.