Recursion

Recursion

Photo by Mel on Unsplash

Introduction

Recursion

  • When a function calls itself

  • until a specified condition is met.

f(){
    print(1);
    f();
}

Stack Space

Base Case

The condition that stops the recursion is called the base case.

//cnt is a global variable
f(){
    if(cnt ==4) return;
    print(cnt);
    cnt++;
    f();
}

Recursion Tree

Instead of showing the entire code in each step of recursion, we can simplify it by using just the function name and visualizing this as a recursion tree.

Basic Problems

Print Name 5 times

  public void f(int i, int N){
      if(i>N) return; //Base Condition
      System.out.print("name");
      f(i+1,N);
  }
public static void main(String args[]){
    int n = 3;
    f(1,n);
}

Print linearly from 1 to N

  public void f(int i, int N){
      if(i>N) return;
      System.out.println(i);
      f(i+1,N);
  }
//f(1,N)

Print from N to 1

  public void f(int i, int N){
      if(i<1) return;
      System.out.println(i);
      f(i-1,N);
  }
//f(N,N)

Backtracking

Print Linearly from 1 to N

  public void f(int i, int N){
      if(i<1) return;
      f(i-1,N);
      System.out.println(i);
  }
//f(N,N)

Print from N to 1

  public void f(int i, int N){
      if(i>N) return;
      f(i+1,N);
      System.out.println(i);
  }
//f(1,N)

Difference between forward-tracking and Back tracking

  • Forward-Tracking: In this approach, we complete the execution of all statements in the current step before making the recursive call. This means we finish each step entirely before moving on to the next one.

  • Backtracking: Here, we make multiple recursive calls from the first step to the last without completing the current step. Once we reach the last step, we start finishing each step in reverse order, moving back to the first step.

Note: Forward-Tracking + BackTracking both exists in any recursion.

Types of Recursion

Parameterized Recursion

Sum of n natural numbers

f(i,sum){
    if(i<1) {
        print(sum);
        return;
    }
    f(i-1, sum+i);
}
//f(n,0)

Functional Recursion

Sum of n natural numbers

f(n){
    if(n==0) return 0;
    return n + f(n-1);
}
//f(n)

Factorial of n

fact(n){
    if(n==0) return 1;
    return n * fact(n-1);
}
//fact(n)

Problems on Functional Recursion

Reverse an Array

We place two pointers, left (l) and right (r), at the start (index 0) and end (index n-1) of the array, respectively. We then swap the elements at these pointers, increment left by 1, and decrement right by 1. This process continues until left surpasses or equals right.

f(l,r){
    if(l>=r) return;
    swap(a[l],a[r]);
    f(l+1,r-1);
}
//f(0,n-1);
f(i){
    if(i>=n/2) return;
    swap(a[i],a[n-1-i]);
    f(i+1);
}
//f(0);

Pallindrome

To check if a string ( s ) is a palindrome, we compare the character at index ( i ) with the character at index ( n-1-i ). We increment ( i ) from 0 to ( n/2 ) (the midpoint). If all corresponding characters match, we return true; otherwise, we return false.

f(i){
    if(i>=n/2) return true;
    if(s[i]!=s[n-1-i]) return false;
    return f(i+1);
}
//f(0);

Valid Pallindrome - Leetcode

A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.

Given a string s, return true if it is a palindrome*, or* false otherwise. (link)

"A man, a plan, a canal: Panama"
Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.

Solution

class Solution {
    public boolean findOutPalindrome(int i, String s){
        int n = s.length();
        if(i>=n/2) return true;
        if(s.charAt(i)!=s.charAt(n-i-1)) return false;
        return findOutPalindrome(i+1, s);
    }
    public boolean isPalindrome(String s) {
        s = refineString(s);
        return findOutPalindrome(0, s);
    }

    public String refineString(String s){
        //a-z 97<->122
        //0 - 9 48<->57
        //space - 32
        // int n = (int) ' ';
        // System.out.println(n);
        s = s.toLowerCase().replaceAll("\\s","");
        StringBuilder root = new StringBuilder();
        for(char ch : s.toCharArray()){
            int ascii = (int) ch;
            if((97<=ascii && ascii<=122) || (48<=ascii && ascii<=57)){
                root.append(ch);
            }
        }
        return root.toString();
    }
}

Multiple Functional Recursion Calls

If a function calls itself multiple times within its body, it is considered to be making multiple recursive calls.

f(){
    ...
    ...
    f()
    ...
    ...
}

Fibonacii Number

The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is, (link)

F(0) = 0, F(1) = 1
F(n) = F(n - 1) + F(n - 2), for n > 1.

Given n, calculate F(n).

Example 1:

Input: n = 2
Output: 1
Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1.

Example 2:

Input: n = 3
Output: 2
Explanation: F(3) = F(2) + F(1) = 1 + 1 = 2.

Solution

  • Pseduo code
f(){
    if(n<=1) return n;
    last = f(n-1)
    slast = f(n-2)
    return last+slast;
}
  • Code

    public int fib(int n) {
        if(n<=1) return n;
        return fib(n-1) + fib(n-2);
    }

Recursion Tree - Multiple recursion calls

Time Complexity - O(2^n)

The function f(n) requires 2 calls, f(n-1) also requires 2 calls, and f(n-2) needs 2 calls as well. Therefore, the total number of calls is ( 2 x 2 x ..... ) (n times), which equals 2^n. However, it takes fewer than 2^n calls because the second call reduces the problem size from n to n-2.