DSA Notes
Understand tail recursion completely — what makes a recursive call a tail call, why tail recursion can be optimized to O(1) stack space, how to convert recursive functions to tail-recursive form using an accumulator, tail-call optimization in different languages, and manual conversion to iteration.
What Is Tail Recursion?
A function call is a tail call if it is the very last operation performed by the calling function — nothing happens after the recursive call returns.
# NOT tail recursive — multiplication happens AFTER recursive call returns
def factorial(n):
if n <= 1: return 1
return n * factorial(n - 1) # ← Must wait for result, then multiply
# ↑
# This multiplication happens AFTER factorial(n-1) returns
# So factorial(n-1) is NOT the last operation
# IS tail recursive — nothing happens after the recursive call
def factorial_tail(n, accumulator=1):
if n <= 1: return accumulator
return factorial_tail(n - 1, n * accumulator) # ← Last operation is the call
# ↑
# The multiplication n * accumulator happens BEFORE the call
# After the call, we just pass its result up — no extra workThe key distinction: in a tail call, the current stack frame is no longer needed after the call, because we are simply returning whatever the recursive call returns.
Language Support for TCO
Languages WITH guaranteed TCO
✅ Scheme / Racket (LISP family) — guaranteed by standard
✅ Scala — @tailrec annotation ensures optimization
✅ Kotlin — tailrec keyword
✅ Clojure — recur keyword
✅ Elixir / Erlang — native support
✅ Haskell — lazy evaluation, tail call safe
Languages WITHOUT TCO
❌ Python — explicitly decided NOT to support TCO
❌ Java — no TCO (JVM limitation)
❌ JavaScript — TCO was in ES6 spec but not widely implemented
⚠️ C/C++ — compiler may optimize (not guaranteed)
Python's stance: Guido van Rossum (Python's creator) deliberately chose not to implement TCO because it would remove helpful stack traces from error messages.
This means in Python, tail recursion does NOT automatically save space — you must manually convert to iteration.
The Accumulator Pattern — Converting to Tail Recursion
The standard technique for making a function tail-recursive: add an accumulator parameter that carries the "work done so far."
Factorial
# Regular recursion: O(n) space
def factorial(n):
if n <= 1: return 1
return n * factorial(n - 1)
# Tail recursive with accumulator: O(1) space with TCO (O(n) in Python)
def factorial_tail(n, acc=1):
if n <= 1: return acc
return factorial_tail(n - 1, n * acc) # acc accumulates the product
# factorial_tail(5, 1):
# factorial_tail(4, 5) acc = 1*5 = 5
# factorial_tail(3, 20) acc = 5*4 = 20
# factorial_tail(2, 60) acc = 20*3 = 60
# factorial_tail(1, 120) acc = 60*2 = 120
# returns 120Sum of List
Fibonacci (Tail Recursive)
# Naive: O(2ⁿ) time
# Tail recursive with two accumulators:
def fib_tail(n, a=0, b=1):
"""
a = F(current_n), b = F(current_n + 1)
"""
if n == 0: return a
return fib_tail(n - 1, b, a + b)
# fib_tail(6):
# fib_tail(5, 1, 1) → a=F(1)=1, b=F(2)=1
# fib_tail(4, 1, 2) → a=F(2)=1, b=F(3)=2
# fib_tail(3, 2, 3) → a=F(3)=2, b=F(4)=3
# fib_tail(2, 3, 5) → a=F(4)=3, b=F(5)=5
# fib_tail(1, 5, 8) → a=F(5)=5, b=F(6)=8
# fib_tail(0, 8, 13) → returns a = 8 = F(6) ✓Reverse a List
Manual Conversion to Iteration (The Real Fix in Python)
Since Python does not support TCO, tail-recursive functions should be converted to iterative loops for production code.
Rule: A tail-recursive function with accumulator is trivially convertible to a while loop.
# Tail-recursive
def factorial_tail(n, acc=1):
if n <= 1: return acc
return factorial_tail(n-1, n*acc)
# Iterative equivalent (same logic, no recursion)
def factorial_iterative(n):
acc = 1
while n > 1: # Replaces the recursive call condition
acc = n * acc # Accumulator update
n = n - 1 # Argument update
return acc
# Pattern: The recursive parameters become loop variables# Tail-recursive Fibonacci
def fib_tail(n, a=0, b=1):
if n == 0: return a
return fib_tail(n-1, b, a+b)
# Iterative equivalent
def fib_iterative(n):
a, b = 0, 1
while n > 0:
a, b = b, a + b
n -= 1
return aTrampoline — Simulating TCO in Python
For very deep recursion where you must keep recursive style, a trampoline pattern avoids stack overflow:
def trampoline(f, *args):
"""
Run a 'thunk-returning' function without growing the call stack.
Instead of calling itself, the function returns a thunk (callable).
The trampoline loop keeps calling thunks until a final value is returned.
"""
result = f(*args)
while callable(result):
result = result()
return result
def factorial_trampoline(n, acc=1):
"""
Returns a thunk (lambda) instead of recursing directly.
Trampoline calls the thunk — no stack growth.
"""
if n <= 1:
return acc
return lambda: factorial_trampoline(n - 1, n * acc)
print(trampoline(factorial_trampoline, 10)) # 3628800
# Works for any n without stack overflow!Scala and Kotlin — Languages with TCO
// Scala — @tailrec annotation verifies and enforces tail recursion
import scala.annotation.tailrec
@tailrec
def factorial(n: Int, acc: Int = 1): Int = {
if (n <= 1) acc
else factorial(n - 1, n * acc) // Compiler optimizes this to a loop
}// Kotlin — tailrec keyword
tailrec fun factorial(n: Int, acc: Int = 1): Int =
if (n <= 1) acc
else factorial(n - 1, n * acc) // Compiled as a loopSummary
| Feature | Regular Recursion | Tail Recursion |
|---|---|---|
| Stack per call | New frame added | Reused (with TCO) |
| Space | O(depth) | O(1) with TCO |
| Pattern | Work done AFTER call | Work done BEFORE call |
| Accumulator | Not needed | Carries partial result |
| Python | O(n) space | O(n) space (no TCO) |
| Scala/Kotlin | O(n) space | O(1) space (TCO) |
Key takeaway for Python: Tail recursion does NOT save space in Python. Always convert deep recursion to iteration for production code. Use the trampoline pattern when recursive style is required for deep recursion.
*Next Lesson: Recursion Tree — How to draw and use recursion trees for complexity analysis.*
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Tail Recursion — Definition, Optimization, and Converting to Iteration.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, recursion, tail, tail recursion — definition, optimization, and converting to iteration
Related Data Structures & Algorithms Topics