Optimisation of recursive algorithm in Java
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Recursive algorithms in Java are elegant but can be extremely inefficient due to redundant computations and deep call stacks. The three main optimization techniques are memoization (caching results of subproblems), converting to iterative solutions (eliminating stack overhead), and tail-call optimization (though the JVM does not optimize tail calls automatically). Understanding when and how to apply these transforms is critical for production Java code.
The Problem: Exponential Fibonacci
fib(5) calls fib(3) twice, fib(2) three times, and fib(1) five times. The same subproblems are solved repeatedly.
Fix 1: Memoization (Top-Down DP)
Cache results in a HashMap or array to avoid recomputation:
With an array instead of HashMap for better performance:
Fix 2: Iterative Solution (Bottom-Up DP)
Eliminate the call stack entirely by computing bottom-up:
No stack overflow risk, no HashMap overhead, minimal memory.
Fix 3: Tail Recursion Conversion
Convert to tail-recursive form (accumulator pattern):
Note: The JVM does not optimize tail calls. The tail-recursive version still uses O(n) stack in Java. Convert to a loop for real optimization:
Fix 4: Stack-Based Iteration (for Tree Recursion)
Replace recursive tree traversal with an explicit stack:
Fix 5: Divide and Conquer Optimization
Merge sort is recursive but already efficient. However, for small subarrays, switch to insertion sort:
Java's Arrays.sort() uses this hybrid approach (TimSort).
Java-Specific Considerations
Stack Size
ConcurrentHashMap for Thread-Safe Memoization
Warning: computeIfAbsent with recursive calls can deadlock on ConcurrentHashMap due to bucket locking. Use a regular HashMap for single-threaded memoization.
Real-World Example: Path Counting
Common Pitfalls
- StackOverflowError: Java's default stack is small. Recursive depth over roughly 5,000-10,000 frames crashes. Convert deep recursion to iteration or increase thread stack size.
- HashMap overhead for memoization: For integer keys, use an array (
long[n+1]) instead ofHashMap<Integer, Long>. Arrays are faster and use less memory due to no boxing. - ConcurrentHashMap.computeIfAbsent deadlock: Recursive calls inside
computeIfAbsentcan deadlock when two keys hash to the same bucket. Use a plainHashMapor pre-populate the cache. - Assuming JVM optimizes tail calls: Unlike Scala or Kotlin (with
tailrec), Java does not optimize tail recursion. Always convert tail-recursive methods to loops manually. - Forgetting base cases: Missing or incorrect base cases cause infinite recursion. Always verify that every recursive path eventually reaches a base case.
Summary
- Memoization transforms exponential recursion to polynomial time by caching subproblem results
- Bottom-up iteration (tabulation) eliminates call stack overhead entirely
- Use arrays instead of HashMap for integer-keyed memoization
- Replace recursive tree/graph traversals with explicit stack for deep structures
- Java does not optimize tail calls. Convert tail recursion to loops manually
- For hybrid approaches, switch to simple algorithms (insertion sort) for small inputs
Related reading
- Optimising accuracy for OneClassSVM
- Optimising the drawing of overlapping rectangles
- Optimization from partial solution minimize sum of distances between pairs
- Optimize Divide an array into continuous subsequences of length no greater than k such that sum of maximum value of each subsequence is minimum
- Optimising caret for sensitivity still seems to optimise for ROC
- Optimization Techniques for C
- Optional environment variables in Spring app
- Optional.get without isPresent check

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.