Recursion in Java, as in other programming languages, is a technique where a method calls itself to solve a problem. This approach can simplify the code for problems that have a natural recursive structure, such as mathematical computations (factorials, Fibonacci series), tree traversal, and more.
The factorial of a non-negative integer n
is the product of all positive integers less than or equal to n
. The factorial function can be defined recursively:
n! = n × (n−1)!
The base case is 0! = 1
public class RecursionExample {
// Recursive method to calculate factorial
public static int factorial(int n) {
if (n == 0) { // Base case
return 1;
} else { // Recursive case
return n * factorial(n - 1);
}
}
public static void main(String[] args) {
int number = 5;
int result = factorial(number);
System.out.println("Factorial of " + number + " is " + result); // Output: Factorial of 5 is 120
}
}
The Fibonacci series is another classic example of recursion. The n-th
Fibonacci number is the sum of the (n−1)-th
and (n−2)-th
Fibonacci numbers. The base cases are:
F(0)=0
F(1)=1
public class FibonacciExample {
// Recursive method to calculate nth Fibonacci number
public static int fibonacci(int n) {
if (n == 0) { // Base case
return 0;
} else if (n == 1) { // Base case
return 1;
} else { // Recursive case
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
public static void main(String[] args) {
int n = 10;
int result = fibonacci(n);
System.out.println("Fibonacci number at position " + n + " is " + result); // Output: Fibonacci number at position 10 is 55
}
}
Recursion is often used to traverse tree structures. For example, a binary tree can be traversed in preorder, inorder, and postorder using recursion.
class Node {
int value;
Node left, right;
Node(int value) {
this.value = value;
left = right = null;
}
}
public class BinaryTree {
Node root;
// Recursive method for inorder traversal
void inorder(Node node) {
if (node != null) {
inorder(node.left); // Visit left subtree
System.out.print(node.value + " "); // Visit node
inorder(node.right); // Visit right subtree
}
}
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
tree.root = new Node(1);
tree.root.left = new Node(2);
tree.root.right = new Node(3);
tree.root.left.left = new Node(4);
tree.root.left.right = new Node(5);
System.out.println("Inorder traversal of binary tree:");
tree.inorder(tree.root); // Output: 4 2 5 1 3
}
}
Tail recursion is a special case of recursion where the recursive call is the last operation in the method. Tail-recursive methods can be optimized by the compiler to avoid adding a new frame to the call stack for each call, which can prevent stack overflow for deep recursions.
public class TailRecursionExample {
// Tail-recursive method to calculate factorial
public static int factorial(int n, int accumulator) {
if (n == 0) {
return accumulator;
} else {
return factorial(n - 1, n * accumulator);
}
}
public static void main(String[] args) {
int number = 5;
int result = factorial(number, 1); // Initial accumulator is 1
System.out.println("Factorial of " + number + " is " + result); // Output: Factorial of 5 is 120
}
}
import java.util.HashMap;
import java.util.Map;
public class FibonacciMemoization {
private static Map<Integer, Integer> memo = new HashMap<>();
public static int fibonacci(int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else if (memo.containsKey(n)) {
return memo.get(n);
} else {
int result = fibonacci(n - 1) + fibonacci(n - 2);
memo.put(n, result);
return result;
}
}
public static void main(String[] args) {
int n = 10;
int result = fibonacci(n);
System.out.println("Fibonacci number at position " + n + " is " + result); // Output: Fibonacci number at position 10 is 55
}
}
Recursion is a powerful tool in Java that can simplify the implementation of complex algorithms. By understanding its principles, advantages, and limitations, and by employing strategies such as memoization and tail recursion, you can effectively leverage recursion to solve various problems efficiently and elegantly.