Skip to main content

Functions

Recursion

Complete lesson for Recursion using LEARNCODEGURU’s 6-level learning model — from basics to advanced practice, with C++ OOP and STL where relevant.

L1

Basics — Recursion

Section: Functions. Recursion in C++ — base case, recursive case, call stack, applications, and classic examples.

What is recursion?

  • **Recursion** is when a function **calls itself** (directly or indirectly) to solve a problem by breaking it into smaller similar subproblems.
  • Every useful recursive function needs two parts: a **base case** (stop) and a **recursive case** (call with a smaller input).
  • Each call waits for the smaller call to finish — results combine as the call stack unwinds.
  • **Real-life analogy:** opening nested gift boxes — open the outer box, then the next, until the last empty/base box, then close them in reverse.
  • **Real-life analogy:** a manager delegates a task to a junior, who delegates further, until someone does the tiny base job and reports back up.
PartRoleExample (factorial)
Base caseStops further callsif (n <= 1) return 1;
Recursive caseCalls itself with smaller inputreturn n * fact(n - 1);
ProgressMust move toward the basen decreases each call
UnwindingCombine results returning up5*4*3*2*1

Recursion — flow, examples, and use cases

Recursion solves a problem by calling the same function on a smaller input until a base case stops. The diagram covers the call flow, factorial / Fibonacci / sum / Tower of Hanoi, recursion vs iteration, and common applications.

C++ recursion infographic: definition, flowchart, recursion vs iteration, factorial call stack, Fibonacci tree, sum of N, Tower of Hanoi, stack overflow warning, and use cases like trees, backtracking, and divide and conquer.
C++ recursion — base case + recursive case; think, break down, recur, solve
  • Base case: stops further calls (e.g. n <= 1).
  • Recursive case: call with a smaller input and combine results.
  • Stack: each call uses a frame — too deep causes stack overflow.
  • Use when: trees, graphs, backtracking, divide & conquer, puzzles.

How recursion works (call stack)

  • Each recursive call pushes a new **stack frame** (parameters, locals, return address).
  • When the base case returns, frames pop and values flow back to the callers.
  • Too many frames → **stack overflow** (missing/wrong base case, or huge depth).
  • Trace with a small n on paper before coding larger cases.
// Trace factorial(4):
// fact(4) -> 4 * fact(3)
// fact(3) -> 3 * fact(2)
// fact(2) -> 2 * fact(1)
// fact(1) -> 1          // base
// then 2*1, 3*2, 4*6 => 24

Factorial (classic example)

  • n! = n × (n−1) × … × 1, with 0! = 1 and 1! = 1.
  • **Real-life:** arranging n distinct books on a shelf (permutations of n items ≈ related counting ideas).
#include <iostream>
using namespace std;

long long factorial(int n) {
    if (n == 0 || n == 1) return 1; // base case
    return n * factorial(n - 1);    // recursive case
}

int main() {
    cout << factorial(5) << "\n"; // 120
    return 0;
}

Fibonacci series

  • fib(0)=0, fib(1)=1, fib(n)=fib(n-1)+fib(n-2).
  • Simple recursion is clear but **slow** for large n (many repeated calls) — later use memoization/DP or iteration.
  • **Real-life:** idealized rabbit-pair growth model; also appears in nature-ish patterns and algorithms.
#include <iostream>
using namespace std;

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

int main() {
    for (int i = 0; i <= 7; i++) cout << fib(i) << " ";
    // 0 1 1 2 3 5 8
    return 0;
}

Sum of first N naturals

  • sum(n) = n + sum(n-1), base sum(0)=0.
  • **Real-life:** total score after n rounds if you add the latest round to the sum of previous rounds.
int sum(int n) {
    if (n == 0) return 0;
    return n + sum(n - 1);
}
// sum(5) = 15

Tower of Hanoi

  • Move n disks from peg A to C using helper B — never put a larger disk on a smaller one.
  • Recursive idea: move n-1 to helper, move largest to target, move n-1 onto target.
  • **Real-life / puzzle:** classic temple puzzle; teaches divide-and-conquer thinking.
  • Number of moves = 2^n - 1 (for n=3 → 7 moves).
#include <iostream>
using namespace std;

void tower(int n, char from, char to, char aux) {
    if (n == 0) return; // base
    tower(n - 1, from, aux, to);
    cout << "Move disk " << n << " from " << from << " to " << to << "\n";
    tower(n - 1, aux, to, from);
}

int main() {
    tower(3, 'A', 'C', 'B');
    return 0;
}

Applications of recursion (with examples)

  • Use recursion when the problem **naturally splits** into smaller copies of itself.
  • Especially strong for trees, graphs, backtracking, and divide-and-conquer algorithms.
Application areaWhy recursion fitsConcrete example
MathematicsDefinition refers to smaller nFactorial, Fibonacci, GCD (Euclid)
Divide & conquerSplit, solve, combineMerge sort, quick sort, binary search
TreesChild nodes are smaller treesInorder / preorder / postorder traversal
GraphsExplore neighbors recursivelyDFS path finding
BacktrackingTry → recurse → undoN-Queens, maze path, sudoku
Parsing / nested dataNested structureNested folders, nested lists, JSON-like trees
PuzzlesState reduces step by stepTower of Hanoi
Dynamic programmingRecursion + memoizationFib with cache, grid paths
String problemsShrink string each callPalindrome check, reverse string
CombinatoricsChoose / skip decisionsGenerate subsets / permutations

Application examples in code

  • **GCD (Euclid):** gcd(a,b)=gcd(b,a%b) until b==0.
  • **Palindrome:** compare ends, recurse on the inner substring/indices.
  • **Binary search:** mid compare, recurse left or right half.
  • **Folder size (idea):** size(folder) = sum of size(child) — tree recursion.
#include <iostream>
#include <string>
using namespace std;

int gcd(int a, int b) {
    if (b == 0) return a; // base
    return gcd(b, a % b);
}

bool isPalindrome(const string& s, int l, int r) {
    if (l >= r) return true; // base
    if (s[l] != s[r]) return false;
    return isPalindrome(s, l + 1, r - 1);
}

int binarySearch(int a[], int lo, int hi, int key) {
    if (lo > hi) return -1; // base: not found
    int mid = lo + (hi - lo) / 2;
    if (a[mid] == key) return mid;
    if (key < a[mid]) return binarySearch(a, lo, mid - 1, key);
    return binarySearch(a, mid + 1, hi, key);
}

int main() {
    cout << gcd(48, 18) << "\n"; // 6
    string w = "level";
    cout << (isPalindrome(w, 0, (int)w.size() - 1) ? "Yes\n" : "No\n");
    int a[] = {1, 3, 5, 7, 9};
    cout << binarySearch(a, 0, 4, 7) << "\n"; // 3
    return 0;
}

Differences between recursion and iteration

  • Both can repeat work; choose based on clarity, depth, and performance.
PointRecursionIteration (loops)
ApproachFunction calls itselfUses for / while
Code sizeOften shorter for trees/HanoiOften longer for nested structures
MemoryExtra stack framesUsually constant extra memory
RiskStack overflow if too deepNo call-stack growth from calls
SpeedCan be slower (call overhead / repeats)Often faster for simple counts
Best forTrees, graphs, backtracking, D&CSimple counting / linear scans

When to use recursion vs a loop

  • Prefer recursion when the data/problem is hierarchical or self-similar.
  • Prefer loops for simple 1..n accumulation, scanning arrays, and when depth may be huge.
  • You can often rewrite recursion as iteration with an explicit stack — advanced but useful.
L2

Advanced — Recursion

Direct vs indirect recursion

  • **Direct:** f calls f.
  • **Indirect:** f calls g, and g calls f (or a longer cycle).
  • Same rules: clear base cases and progress toward termination.
bool isEven(int n);
bool isOdd(int n);

bool isEven(int n) {
    if (n == 0) return true;
    return isOdd(n - 1);
}
bool isOdd(int n) {
    if (n == 0) return false;
    return isEven(n - 1);
}

Tail recursion and stack pressure

  • A **tail-recursive** call is the last action in the function — some languages optimize it; C++ does **not** guarantee TCO.
  • Deep recursion on large n can still overflow — use iteration or increase design limits carefully.
  • Naive Fibonacci is exponential time — add **memoization** (cache results) or use a loop.
#include <vector>
using namespace std;

long long fibMemo(int n, vector<long long>& memo) {
    if (n <= 1) return n;
    if (memo[n] != -1) return memo[n];
    return memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
}

Backtracking pattern (application)

  • Choose an option → recurse → undo the choice (backtrack) → try next option.
  • **Real-life:** maze — walk a path, hit a dead end, step back, try another corridor.
  • Used in N-Queens, permutations, subset sum, crossword-like fills.
#include <iostream>
#include <vector>
using namespace std;

void subsets(vector<int>& nums, int i, vector<int>& cur) {
    if (i == (int)nums.size()) {
        cout << "{ ";
        for (int x : cur) cout << x << " ";
        cout << "}\n";
        return;
    }
    // exclude nums[i]
    subsets(nums, i + 1, cur);
    // include nums[i]
    cur.push_back(nums[i]);
    subsets(nums, i + 1, cur);
    cur.pop_back(); // backtrack
}

Tree recursion (application idea)

  • A tree node’s work often = process node + recurse(left) + recurse(right).
  • **Real-life:** company org chart — total headcount(manager) = 1 + sum(headcount(report)).
  • **Real-life:** disk usage — folder size = file sizes + size of subfolders.

Safety checklist

  • Always write the base case first.
  • Ensure every recursive path moves closer to the base (smaller n, narrower range, shorter string).
  • Test n=0, n=1, and one medium value with a paper trace.
  • Watch integer overflow for factorial / fib of larger inputs (long long).
  • If depth may be thousands+, prefer iteration or an explicit stack.
L3

Tricky Interview Points

Q1) What is recursion?

Answer A technique where a function solves a problem by calling itself on a smaller subproblem until a base case stops the calls.

Q2) What are base case and recursive case?

Answer Base case returns a result without further calls. Recursive case calls the function again with a smaller/simpler input and combines the result.

Q3) What happens if there is no base case?

Answer Recursion never stops; the call stack grows until stack overflow (or the program is killed).

Q4) Recursion vs iteration?

Answer Recursion uses function calls and stack memory; iteration uses loops and usually constant extra memory. Recursion is often clearer for trees/backtracking; loops are often better for simple linear tasks.

Q5) What is a call stack in recursion?

Answer The runtime stack of frames for each active call. Frames push on call and pop on return, which is how values unwind.

Q6) Give applications of recursion.

Answer Factorial/Fibonacci/GCD, Tower of Hanoi, binary search, tree/graph DFS, merge/quick sort, backtracking (N-Queens, maze), generating subsets/permutations, nested folder processing.

Q7) Why is naive recursive Fibonacci slow?

Answer It recomputes the same subproblems many times (exponential calls). Fix with memoization/DP or an iterative loop.

Q8) What is Tail recursion?

Answer When the recursive call is the last operation in the function. Some languages optimize it; C++ does not guarantee tail-call optimization.

Q9) How many moves does Tower of Hanoi need for n disks?

Answer 2^n − 1 moves (n=3 → 7).

Q10) Direct vs indirect recursion?

Answer Direct: a function calls itself. Indirect: functions call each other in a cycle (f→g→f).

Q11) When should you avoid recursion?

Answer When depth can be very large, when a simple loop is clearer/faster, or when stack space is limited — unless you control depth or convert to iteration.

Q12) How do you reverse a string using recursion?

Answer Swap/print ends and recurse on the inner range, or recurse on the suffix and append the first character on the way back.

L4

Simple Programs

Factorial

#include <iostream>
using namespace std;

long long fact(int n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
}

int main() {
    cout << fact(6) << "\n"; // 720
    return 0;
}

Fibonacci print first N terms

#include <iostream>
using namespace std;

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

int main() {
    int n = 8;
    for (int i = 0; i < n; i++) cout << fib(i) << " ";
    return 0;
}

Sum of digits (recursive)

#include <iostream>
using namespace std;

int sumDigits(int n) {
    if (n == 0) return 0;
    return (n % 10) + sumDigits(n / 10);
}

int main() {
    cout << sumDigits(1234) << "\n"; // 10
    return 0;
}

Power x^n (recursive)

#include <iostream>
using namespace std;

long long power(long long x, int n) {
    if (n == 0) return 1;
    return x * power(x, n - 1);
}

int main() {
    cout << power(2, 10) << "\n"; // 1024
    return 0;
}

Tower of Hanoi (n = 3)

#include <iostream>
using namespace std;

void hanoi(int n, char a, char c, char b) {
    if (n == 0) return;
    hanoi(n - 1, a, b, c);
    cout << "Move " << n << ": " << a << " -> " << c << "\n";
    hanoi(n - 1, b, c, a);
}

int main() {
    hanoi(3, 'A', 'C', 'B');
    return 0;
}

GCD — Euclid recursion

#include <iostream>
using namespace std;

int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main() {
    cout << gcd(84, 30) << "\n"; // 6
    return 0;
}

Recursive binary search

#include <iostream>
using namespace std;

int bsearch(int a[], int lo, int hi, int key) {
    if (lo > hi) return -1;
    int mid = lo + (hi - lo) / 2;
    if (a[mid] == key) return mid;
    if (key < a[mid]) return bsearch(a, lo, mid - 1, key);
    return bsearch(a, mid + 1, hi, key);
}

int main() {
    int a[] = {2, 4, 6, 8, 10, 12};
    cout << bsearch(a, 0, 5, 10) << "\n";
    return 0;
}
L5

Problem Solving Tricks

  • Write the base case first — then the recursive step.
  • Every call must move closer to the base (smaller n / narrower range).
  • Trace n=0,1,2,3 on paper before trusting larger inputs.
  • Prefer long long for factorial/fib growth.
  • Naive fib → memoize or iterate for speed.
  • Recursion shines for trees, backtracking, Hanoi, D&C sorts.
  • Use loops for simple linear scans to save stack and time.
  • Indirect recursion still needs a clear stopping condition.
  • Backtracking = choose → recurse → undo.
  • Stack overflow usually means missing base case or huge depth.
  • Name functions after the problem: factorial, gcd, hanoi — not rec1.
  • Compare recursion vs loop mentally: which maps the problem structure better?
L6

Practice Programs List

  1. Write recursive factorial and verify 0!, 1!, 5!, 10!.
  2. Print the first 10 Fibonacci numbers using recursion.
  3. Recursive sum of first n naturals; compare with formula n(n+1)/2.
  4. Recursive sum of digits of an integer.
  5. Recursive reverse of a positive integer’s digits (print or build a number).
  6. Implement Euclid GCD recursively; test several pairs.
  7. Tower of Hanoi for n=1,2,3 and count the printed moves.
  8. Recursive palindrome check for a string using indices l,r.
  9. Recursive binary search on a sorted array.
  10. Recursive power x^n; then try a faster version using x^(n/2).
  11. Generate all subsets of {1,2,3} with backtracking recursion.
  12. Rewrite factorial with a loop and compare clarity/performance.
  13. Explain in comments a full call-stack trace of factorial(4).
  14. List five real applications of recursion and one example each.