Angle Bracket <> in Java with Examples Different types of Recursions in Java Java Lambda Filter Example Java Program for Shopping Bill Pythagorean Triplet with Given Sum Using Single Loop in Java TemporalAdjusters lastInMonth() method in Java with Examples ZIP API in Java Atomic reference in Java Digit Extraction in Java DRY (Don't Repeat Yourself) Principles in Java with Examples Empty array in Java Is main method compulsory in Java? Java I/O Operation - Wrapper Class vs Primitive Class Variables Java Program to Find the Perimeter of a Rectangle Java Thread Priority Java Type Erasure Need of Concurrent Collections in Java Nested ArrayList in Java Print Odd and Even Numbers by Two Threads in Java Square pattern in Java TemporalAdjusters next() method in Java with Examples What does start() function do in multithreading in Java Convert Number to Words Problem in Java Detect And Remove Cycle in a Single Linked List in Java Evolution of Interfaces in Java How to pad a String in Java Implementing Patricia Trie in Java Java Program to Find the Most Repeated Word in a Text File java.util.UUID class in Java ReadWriteLock Interface in Java Reference Data Types in Java Sort An Array According to The Count of Set Bits in Java Alternate Vowel and Consonant string in Java Built-in Exceptions in Java with Examples Capture the Pawns Problem in Java Collections.shuffle() Method in Java with Examples JDBC MySQL Localhost 3306 Alternate Vowel and Consonant string in Java Built-in Exceptions in Java with Examples Capture the Pawns Problem in Java Collections.shuffle() Method in Java with Examples Convert Number to Words Problem in Java Detect And Remove Cycle in a Single Linked List in Java Evolution of Interfaces in Java How to pad a String in Java Implementing Patricia Trie in Java Java Program to Find the Most Repeated Word in a Text File java.util.UUID class in Java ReadWriteLock Interface in Java Reference Data Types in Java Sort An Array According to The Count of Set Bits in Java JDBC MySQL Localhost 3306 Adding a Character as Thousands Separator to Given Number in Java Circular Primes in Java Equilibrium Index Problem in Java Java String regionMatches() Method with Examples LFU Cache Problem in Java Longest Repeating and Non-Overlapping Substring in Java Prefix Suffix Search in Java Product Array Puzzle Problem in Java Russian Doll Envelopes Problem in Java Second Most Repeated Word in a Sequence in Java Special Two-Digit Numbers in a Binary Search Tree in Java Swap Corner Words and Reverse Middle Characters in Java Toggle K bits Problem in Java Upside Down Binary Tree in Java Verbal Arithmetic Puzzle Problem in Java Insert a String into another String in Java Print Shortest Path to Print a String on Screen in Java Search Insert Position in Java BST Sequences in Java Burrows - Wheeler Data Transform Algorithm in Java Convert BST to Min Heap in Java Fibonacci Sum in Java Get name of current method being executed in Java Keith number in Java Longest Even Length Substring in Java Saint-Exupery Numbers in Java Standard practice for protecting sensitive data in Java application Strobogrammatic number in Java Types of Methods in Java Programming Valid Number Problem in Java Boggle Search Problem in Java Convert Java Object to JSON String using Jackson API Generate Random String in Java Java Program to Determine the Unicode Code Point at Given Index in Strin Java 18 snippet tags Jumping Numbers in Java Junction Numbers in Java Find Four Elements that Sums to a given value in Java How to print string vertically in Java How to remove in string in Java Three-Way Partition Problem in Java Apocalyptic Number in Java Check if the given two matrices are mirror images of one another in Java Duplicate Characters Problem in Java Duplicate Parenthesis Problem in Java Sum of Minimum and Maximum Elements of all Subarrays of Size K in Java Triple Shift Operator in Java Valid Pairing of Numbers in Java Valid Sudoku problem in Java Java Cipher Class Kth Missing Positive Number in Java Largest Square Matrix Problem in Java Length of the longest substring without repeating characters in Java Minimum Cost to Make String Valid Problem in Java Ordered pair in Java Range Addition Problem in Java

Different types of Recursions in Java

Recursion is a programming concept where a function calls itself in order to solve a problem. It's a powerful and elegant technique used in many programming languages, including Java. Recursive solutions are often more concise and expressive for certain types of problems.

Basic Structure of Recursion

A recursive method typically has two parts

1. Base Case

This is the termination condition that stops the recursion. It defines the smallest subproblem that can be solved directly. Without a base case, the recursion would continue indefinitely, leading to a stack overflow.

2. Recursive Case

This is where the method calls itself with a modified argument to break down the problem into smaller subproblems. Each recursive call should bring the problem closer to the base case.

Types of Recursion

1. Linear Recursion

Linear recursion is a type of recursion where a method calls itself exactly once in a linear sequence. The recursive calls form a chain, and each recursive call reduces the problem toward a base case.

Filename: LinearRecursionExample.java

public class LinearRecursionExample {

    // Linear recursive method to calculate factorial

    public static int factorial(int n) {

        // Base case: factorial of 0 is 1

        if (n == 0) {

            return 1;

        } else {

            // Recursive case: n! = n * (n-1)!

            int smallerFactorial = factorial(n - 1);

            return n * smallerFactorial;

        }

    }

    public static void main(String[] args) {

        // Example: Calculate factorial of 5

        int result = factorial(5);

        // Display the result

        System.out.println("Factorial of 5 is: " + result);

    }

}

Output:

Factorial of 5 is: 120

Explanation

The base case checks if n is equal to 0. If true, it returns 1, as the factorial of 0 is 1. In the recursive case, the method calls itself with the argument (n - 1). The result of the recursive call (smallerFactorial) is multiplied by n to get the factorial of n.

2. Tail Recursion

Tail recursion is a special case of recursion where the recursive call is the last operation performed in a method. In tail-recursive functions, the result of the recursive call is immediately returned without any additional computation. This property allows certain compilers to optimize the recursive calls, effectively converting the recursion into an iterative process and avoiding stack overflow.

Filename: TailRecursionExample.java

public class TailRecursionExample {

    // Tail-recursive method to calculate factorial

    public static int factorial(int n, int accumulator) {

        // Base case: factorial of 0 is the accumulator

        if (n == 0) {

            return accumulator;

        } else {

            // Tail recursive call: passing updated values

            return factorial(n - 1, n * accumulator);

        }

    }

    // Helper method to initiate the tail-recursive calculation

    public static int calculateFactorial(int n) {

        // Call the tail-recursive method with initial values

        return factorial(n, 1);

    }

    public static void main(String[] args) {

        // Example: Calculate factorial of 5 using tail recursion

        int result = calculateFactorial(5);

        // Display the result

        System.out.println("Factorial of 5 is: " + result);

    }

}

Output:

Factorial of 5 is: 120

Explanation

The factorial method takes two parameters, n and accumulator. The base case checks if n is equal to 0. If true, it returns the accumulator. In the recursive case, the method calls itself with the updated values (n - 1 and n * accumulator).

The calculateFactorial method serves as a helper to initiate the tail-recursive calculation. It calls the factorial method with the initial values n and an initial accumulator of 1. The tail-recursive call (return factorial(n - 1, n * accumulator);) is the last operation in the method.

This structure allows certain compilers to optimize the recursion into an iterative loop, avoiding stack overflow.

3. Binary Recursion

Binary recursion is a type of recursion where a method makes two recursive calls in each invocation. This often involves dividing the problem into two sub-problems. One of the classic examples of a problem that can be solved using binary recursion is the calculation of the Fibonacci sequence.

Filename: BinaryRecursionExample.java

public class BinaryRecursionExample {

    // Binary recursive method to calculate Fibonacci sequence

    public static int fibonacci(int n) {

        // Base case: Fibonacci of 0 and 1 is 1

        if (n <= 1) {

            return 1;

        } else {

            // Binary recursive call: Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2)

            return fibonacci(n - 1) + fibonacci(n - 2);

        }

    }

    public static void main(String[] args) {

        // Example: Calculate Fibonacci sequence up to the 7th term

        int n = 7;

        System.out.println("Fibonacci sequence up to term " + n + ":");

        for (int i = 0; i < n; i++) {

            System.out.print(fibonacci(i) + " ");

        }

    }

}

Output:

Fibonacci sequence up to term 7:

1 1 2 3 5 8 13

Explanation

The base case checks if n is less than or equal to 1. If true, it returns 1, as the Fibonacci of 0 and 1 is 1. In the recursive case, the method calls itself twice with the arguments n - 1 and n - 2. -The result of the binary recursive calls is summed to get the Fibonacci sequence.

4. Multiple Recursion

Multiple recursion involves more than two recursive calls in each method invocation, and the problem is divided into multiple sub-problems. One classic example that demonstrates multiple recursion is the calculation of the Ackermann function. The Ackermann function is a computationally intensive function that grows rapidly and is often used to test the efficiency of recursive algorithms.

Filename: MultipleRecursionExample.java

public class MultipleRecursionExample {

    // Multiple recursive method to calculate the Ackermann function

    public static int ackermann(int m, int n) {

        // Base case: Ackermann(0, n) = n + 1

        if (m == 0) {

            return n + 1;

        }

        // Base case: Ackermann(m, 0) = Ackermann(m - 1, 1)

        else if (n == 0) {

            return ackermann(m - 1, 1);

        }

        // Multiple recursive case: Ackermann(m, n) = Ackermann(m - 1, Ackermann(m, n - 1))

        else {

            return ackermann(m - 1, ackermann(m, n - 1));

        }

    }

    public static void main(String[] args) {

        // Example: Calculate Ackermann function for m = 3, n = 4

        int result = ackermann(3, 4);

        // Display the result

        System.out.println("Ackermann(3, 4) is: " + result);

    }

}

Output:

Ackermann(3, 4) is: 125

Explanation

The Ackermann function has two base cases. If m is 0, it returns n + 1. If n is 0, it recursively calls itself with m - 1 and 1. In the multiple recursive case, when both m and n are non-zero, the method makes multiple recursive calls. The function calls itself with m - 1 and the result of another Ackermann function call with the same m and n - 1.

5. Nested Recursion

Nested recursion occurs when a function calls itself with the result of a recursive call. In other words, the parameter of the recursive call is the result of another recursive call. A classic example that demonstrates nested recursion is the computation of the Ackermann-Péter function. This function is a variation of the Ackermann function and provides a good illustration of nested recursion.

Filename: NestedRecursionExample.java

public class NestedRecursionExample {

    // Nested recursive method to demonstrate recursion inside recursion

    public static int nestedRecursion(int x) {

        // Base case: if x is greater than 100, return x - 10

        if (x > 100) {

            return x - 10;

        } else {

            // Nested recursive call: recursion inside recursion

            return nestedRecursion(nestedRecursion(x + 11));

        }

    }

    // Driver code

    public static void main(String[] args) {

        // Example: Demonstrate nested recursion with an initial value of 95

        int result = nestedRecursion(95);

        // Display the result

        System.out.println("Result of nested recursion: " + result);

    }

}

Output:

Result of nested recursion: 91

Explanation

The nestedRecursion method takes an integer x as an argument and, based on the value of x, either returns x - 10 if x is greater than 100 or makes a nested recursive call with the argument nestedRecursion(x + 11).

The main method then calls the nestedRecursion method with an initial value of 95 and prints the result as 91.

6. Indirect Recursion

Indirect recursion is a situation where two or more functions are mutually recursive, forming a cycle of calls. This means that Function A calls Function B, and then Function B calls Function A, and so on. In other words, the functions indirectly call each other. This can lead to a cyclic pattern of execution.

Filename: IndirectRecursionExample.java

public class IndirectRecursionExample {

    // Function A in indirect recursion

    public static void functionA(int n) {

        if (n > 0) {

            System.out.println("Function A: " + n);

            functionB(n - 1);

        }

    }

    // Function B in indirect recursion

    public static void functionB(int n) {

        if (n > 1) {

            System.out.println("Function B: " + n);

            functionA(n / 2);

        }

    }

    // Driver code

    public static void main(String[] args) {

        // Example: Demonstrate indirect recursion with an initial value of 5

        functionA(5);

    }

}

Output:

Function A: 5

Function B: 4

Function A: 2

Explanation

functionA takes an integer n as a parameter. If n is greater than 0, it prints a message indicating it's in "Function A" and then calls functionB with n - 1. functionB takes an integer n as a parameter. If n is greater than 1, it prints a message indicating it's in "Function B" and then calls functionA with n / 2.

7. Mutual Recursion

Mutual recursion occurs when two or more functions call each other in a circular manner. In mutual recursion, a group of functions forms a cycle of calls, where each function in the group calls another, and the cycle continues.

Filename: MutualRecursionExample.java

public class MutualRecursionExample {

    // Function A in mutual recursion

    public static void functionA(int n) {

        if (n > 0) {

            System.out.println("Function A: " + n);

            functionB(n - 1);

        }

    }

    // Function B in mutual recursion

    public static void functionB(int n) {

        if (n > 1) {

            System.out.println("Function B: " + n);

            functionC(n / 2);

        }

    }

    // Function C in mutual recursion

    public static void functionC(int n) {

        if (n > 0) {

            System.out.println("Function C: " + n);

            functionA(n - 1);

        }

    }

    // Driver code

    public static void main(String[] args) {

        // Example: Demonstrate mutual recursion with an initial value of 5

        functionA(5);

    }

}

Output:

Function A: 5

Function B: 4

Function C: 2

Function A: 1

Explanation

functionA takes an integer n as a parameter, prints a message, and calls functionB(n - 1). functionB takes an integer n as a parameter, prints a message, and calls functionC(n / 2). functionC takes an integer n as a parameter, prints a message, and calls functionA(n - 1).

The difference between mutual recursion and indirect recursion is that the mutual recursion forms a direct cycle of calls between functions, while indirect recursion involves functions calling each other in a chain-like manner, and the calls might not form a direct cycle.

8. Tree Recursion

Tree recursion is a type of recursion where a function calls itself multiple times in a branching structure, creating a recursive tree. Each recursive call leads to multiple subsequent calls, forming a tree-like pattern. Tree recursion is often used to solve problems that exhibit a hierarchical or nested structure.

Filename: TreeRecursionExample.java

public class TreeRecursionExample {

    // Tree recursive method to print numbers

    public static void printNumbers(int n) {

        if (n > 0) {

            System.out.println(n);

            printNumbers(n - 1);

            printNumbers(n - 1);

        }

    }

    // Driver code

    public static void main(String[] args) {

        // Example: Demonstrate tree recursion with an initial value of 3

        printNumbers(3);

    }

}

Output:

3

2

1

1

2

1

1

Explanation

The printNumbers method takes an integer n as a parameter. If n is greater than 0, it prints the current value of n. It then makes two recursive calls to printNumbers(n - 1), creating a branching structure.

Conclusion

In conclusion, recursion is a powerful and versatile programming technique in Java, offering various types that cater to different problem-solving scenarios. Linear recursion involves a single recursive call, tail recursion optimizes the recursive call position for potential performance gains, binary recursion divides problems into two sub-problems, multiple recursion handles more than two recursive calls, nested recursion involves calls within the parameters of another recursive call, and indirect recursion forms cycles of calls between multiple functions. Mutual recursion sees functions calling each other in a circular manner, while tree recursion unfolds in a branching structure.