Each element doubles the work. Common in recursive solutions without memoization.
example
// JavaScriptfunction fibNaive(n) {
if (n <= 1) return n;
return fibNaive(n - 1) + fibNaive(n - 2);
}
// Each call branches into 2 → O(2^n)# Pythondef fib_naive(n):
if n <= 1:
return n
return fib_naive(n - 1) + fib_naive(n - 2)
# Fix with memoization → O(n)
output
Time: O(2^n) without memo | O(n) with memo | Space: O(n)
Note Generating all subsets of a set is O(2^n). This is a red flag in interviews — if your solution is exponential, look for overlapping subproblems (DP) or pruning (backtracking). Always mention that memoization brings it down.
Memoization (top-down): Recursive + cache results of subproblems.
Tabulation (bottom-up): Iterative + fill a tablefrom base cases up.
Both avoid recomputing overlapping subproblems.
example
// JavaScript — Fibonacci both ways// Top-down (memoization)function fibMemo(n, memo = {}) {
if (n <= 1) return n;
if (n in memo) return memo[n];
memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
return memo[n];
}
// Bottom-up (tabulation)function fibTab(n) {
if (n <= 1) return n;
const dp = [0, 1];
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
# Python# Top-downdef fib_memo(n, memo={}):
if n <= 1:
return n
if n notin memo:
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)
return memo[n]
# Bottom-updef fib_tab(n):
if n <= 1:
return n
dp = [0, 1]
for i in range(2, n + 1):
dp.append(dp[-1] + dp[-2])
return dp[n]
output
fib_memo(10) → 55
fib_tab(10) → 55
Note Both are O(n) time, O(n) space. Memoization is easier to write (natural recursion) but has call stack overhead. Tabulation avoids stack overflow and can be space-optimized (keep only last 2 values → O(1) space). Start with memoization in interviews, then optimize to tabulation if asked.
function memoize(fn) {
const cache = new Map();
returnfunction (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const expensiveCalc = memoize((n) => {
console.log("Computing...");
return n ** 2 + Math.sqrt(n);
});
console.log(expensiveCalc(100)); // "Computing..." then 10010
console.log(expensiveCalc(100)); // 10010 (cached, no log)
Note Caches results for repeated calls with the same arguments. Use JSON.stringify for simple keys. For complex args, consider a WeakMap-based approach.