← All Articles
intermediate30 min

Recursion vs Backtracking vs DP vs Greedy

Learn how to identify which algorithm paradigm to use - a practical comparison guide with decision frameworks, problem patterns, and real examples

Quick Comparison Table

Before diving deep, here's a quick reference to understand how these four paradigms differ. Bookmark this for quick revision!

AspectRecursionBacktrackingDPGreedy
Core IdeaTry everythingTry valid paths onlyCache & reuseBest choice now
Explores All Paths?YesValid onlyNoNo
Stores Results?NoNoYesNo
Undoes Choices?SometimesAlwaysNoNo
Guarantees Optimal?Yes*Yes*YesSometimes
Time ComplexityO(2^N), O(N!)O(2^N) prunedO(N), O(N²)O(N), O(N log N)
Max Input Size≤ 15≤ 20≤ 10⁴≤ 10⁶+

*If all paths are explored; finds all solutions, not necessarily "optimal"

One-Liner Definitions

🔴

Recursion

Divide problem into smaller subproblems, solve each the same way

🟠

Backtracking

Recursion + undo invalid choices (Choose → Explore → Undo)

🔵

Dynamic Programming

Recursion + cache results to avoid recomputation

🟢

Greedy

Make the locally optimal choice at each step, never look back

How They Relate

                    RECURSION
                   (base technique)
                        │
          ┌─────────────┼─────────────┐
          │             │             │
          ▼             ▼             ▼
    BACKTRACKING       DP          GREEDY
    (+ pruning)    (+ caching)   (no exploration)
    (+ undo)

    "All valid      "Optimal      "One path,
     solutions"      answer"       fast"

Key Insight

Backtracking and DP are both extensions of recursion.Backtracking adds pruning and undoing. DP adds memoization. Greedy abandons exploration entirely and just picks the best local choice.

Key Differences Explained

Let's understand the fundamental differences through the lens of the same problem: finding paths in a decision tree.

The Decision Tree Mental Model

Problem: Generate subsets of [1, 2]

                    []
                   /  \
            Include 1   Exclude 1
                /           \
              [1]            []
             /   \          /   \
       Inc 2   Exc 2   Inc 2   Exc 2
         /       \       /       \
      [1,2]     [1]    [2]       []

All leaf nodes = All subsets: [], [1], [2], [1,2]

Difference 1: What Gets Explored

Recursion

Explores every single path in the tree, even invalid ones.

Visits: ALL 4 leaf nodes

Backtracking

Explores only valid paths, prunes invalid branches early.

Visits: Only valid nodes (pruned)

DP

Explores unique states only, caches repeated subproblems.

Visits: Each unique state once

Greedy

Explores exactly one path, makes irrevocable choices.

Visits: Single path (no tree)

Difference 2: Where the Answer Appears

ParadigmAnswer LocationExample
RecursionAt leaf nodesBase case returns value
BacktrackingAnywhere on pathCollect valid combinations mid-way
DPAt the rootFinal answer bubbles up
GreedyAccumulated along pathBuild answer step by step

Difference 3: Code Pattern

Recursion Pattern

Java
1void recurse(state) {
2    if (base_case) {
3        process_answer();  // Answer at leaf
4        return;
5    }
6    for (choice : choices) {
7        recurse(next_state);  // No undo
8    }
9}

Backtracking Pattern

Java
1void backtrack(state, path) {
2    if (is_valid_solution(path)) {
3        collect_answer(path);  // Answer anywhere
4    }
5    for (choice : choices) {
6        if (is_valid(choice)) {   // Pruning
7            path.add(choice);     // Choose
8            backtrack(next_state, path);
9            path.remove(last);    // Undo ← KEY DIFFERENCE
10        }
11    }
12}

DP Pattern (Top-Down)

Java
1int dp(state) {
2    if (base_case) return base_value;
3    if (memo[state] != -1) return memo[state];  // Cache hit
4
5    int result = combine(dp(subproblem1), dp(subproblem2));
6    memo[state] = result;  // Store ← KEY DIFFERENCE
7    return result;  // Answer bubbles to root
8}

Greedy Pattern

Java
1int greedy(input) {
2    sort_if_needed(input);
3    int answer = 0;
4
5    for (item : input) {
6        if (should_take(item)) {  // Local optimal
7            answer += item;       // Accumulate
8        }                         // No recursion, no undo
9    }
10    return answer;
11}

Difference 4: Same Problem, Different Questions

Problem: Climbing Stairs (1 or 2 steps at a time)

Recursion:"Try all possible ways" → O(2^N), TLE for N > 30
Backtracking:"List all paths" → Useful if you need actual paths, not count
DP:"How many ways?" → O(N), the right approach
Greedy:Doesn't apply - no local optimal property

The Core Insight

The type of question determines the approach:

  • "Find all solutions" → Backtracking
  • "Find the count/optimal" + overlapping subproblems → DP
  • "Find onesolution fast" + greedy property → Greedy

How to Identify the Right Approach

This is the most important section. Learn to recognize problem patterns and keywords that instantly reveal which paradigm to use.

Decision Flowchart

START: Read the problem
          │
          ▼
┌─────────────────────────────┐
│ Does it ask for ALL         │
│ combinations/permutations/  │──YES──▶ BACKTRACKING
│ subsets/paths?              │
└─────────────────────────────┘
          │ NO
          ▼
┌─────────────────────────────┐
│ Does it ask for COUNT or    │
│ OPTIMAL (min/max) value?    │──YES──┐
└─────────────────────────────┘       │
          │ NO                        ▼
          │              ┌─────────────────────────┐
          │              │ Are there OVERLAPPING   │
          │              │ subproblems?            │
          │              └─────────────────────────┘
          │                    │ YES        │ NO
          │                    ▼            ▼
          │                   DP      Plain Recursion
          ▼
┌─────────────────────────────┐
│ Can you prove local optimal │
│ = global optimal?           │──YES──▶ GREEDY
└─────────────────────────────┘
          │ NO
          ▼
    Consider DP or
    Brute Force

Keyword Recognition Guide

🟠 Backtracking Keywords

all combinationsall permutationsall subsetsall pathsgenerate allfind alllist allprint allN-QueensSudoku solver

Look for: "all", "every", "generate", "enumerate", constraint satisfaction

🔵 DP Keywords

minimum costmaximum profitnumber of wayslongest/shortestcan you reachis it possiblecount pathsoptimalbest

Look for: optimization, counting, yes/no questions with overlapping choices

🟢 Greedy Keywords

maximum meetingsminimum platformsactivity selectioninterval schedulingfractionalalways pick largest/smallestsort and process

Look for: intervals, scheduling, "obvious" local choice, large constraints (10⁵+)

Problem Type → Approach

Problem TypeApproachExample Problems
Generate all XBacktrackingSubsets, Permutations, Combinations
Constraint satisfactionBacktrackingN-Queens, Sudoku, Word Search
Count number of waysDPClimbing Stairs, Unique Paths, Coin Change II
Min/Max optimizationDPKnapsack, LCS, Edit Distance
Yes/No reachabilityDPWord Break, Partition Equal Subset
Interval schedulingGreedyMeeting Rooms, Activity Selection
Fractional selectionGreedyFractional Knapsack, Gas Station
Graph shortest pathGreedy (Dijkstra)Network Delay, Cheapest Flights

Quick Mental Tests

Is it Backtracking?

  • Need to generate/list ALL solutions?
  • Constraints are small (N ≤ 15-20)?
  • Choices have validity rules?

Is it DP?

  • Solving same subproblem multiple times?
  • Optimal answer = f(optimal sub-answers)?
  • Asking for count or min/max?

Is it Greedy?

  • Local best choice leads to global best?
  • No need to reconsider past decisions?
  • Problem involves intervals or sorting?

DP vs Greedy?

  • Greedy: choices don't affect future options
  • DP: current choice affects future choices
  • When in doubt, try DP (always correct)

Interview Golden Rules

1

"ALL" in problem statement → Backtracking
Find all, generate all, list all, print all

2

"COUNT" or "OPTIMAL" + overlapping → DP
Number of ways, minimum cost, maximum profit, longest/shortest

3

Large constraints (N ≥ 10⁵) → Greedy or O(N) approach
DP won't work, must find greedy property or linear solution

4

Greedy feels "too easy" → Verify or use DP
Test with counter-examples before committing

Pro Tip: Start with Brute Force

In interviews, always start by explaining the brute force recursive solution first. Then optimize: add pruning (backtracking) or memoization (DP) based on the problem pattern. This shows your problem-solving process.

Constraints-Based Selection

The input size constraint is often the biggest hint about which approach will work. Learn to read constraints like a roadmap to the solution.

The Golden Rule

Constraints decide feasibility. If N = 10⁵, you can't use O(2^N). Time limit (usually 1-2 sec) means ~10⁸ operations max.

Constraint → Approach Mapping

N (Input Size)Max ComplexityLikely ApproachWhy
≤ 10O(N!)Backtracking10! = 3.6M operations
≤ 20O(2^N)Backtracking / Bitmask2^20 = 1M operations
≤ 100O(N³)DP100³ = 1M operations
≤ 1,000O(N²)DP1000² = 1M operations
≤ 10,000O(N²) carefulDP / Two Pointers10⁴² = 100M (borderline)
≤ 10⁵O(N log N)Greedy / Binary SearchO(N²) will TLE
≤ 10⁶O(N)Greedy / Sliding WindowOnly linear works
≤ 10⁹O(log N)Math / Binary SearchCan't even iterate all

Time Complexity Reference

Operations per Complexity

O(1)1 operation
O(log N) for N=10⁹~30 operations
O(N) for N=10⁶1M operations
O(N log N) for N=10⁵~1.6M operations
O(N²) for N=10³1M operations
O(N³) for N=1001M operations
O(2^N) for N=20~1M operations
O(N!) for N=10~3.6M operations

Safe Limits (1 sec)

O(N!)N ≤ 10-11
O(2^N)N ≤ 20-25
O(N³)N ≤ 400-500
O(N²)N ≤ 5,000-10,000
O(N log N)N ≤ 10⁶
O(N)N ≤ 10⁷-10⁸
O(log N)N ≤ 10¹⁸

Real Problem Examples

N ≤ 10Permutations (LeetCode 46)

Generate all permutations of N distinct integers. N! grows fast, but 10! = 3.6M is fine.→ Backtracking

N ≤ 1000Longest Common Subsequence (LeetCode 1143)

Two strings, each up to 1000 chars. O(N²) = 1M operations works perfectly.→ DP

N ≤ 10⁵Meeting Rooms II (LeetCode 253)

Find minimum meeting rooms for N intervals. O(N²) would TLE. Sort + sweep works.→ Greedy (O(N log N))

N ≤ 10⁶Maximum Subarray (LeetCode 53)

Find max sum contiguous subarray. Must be O(N) - Kadane's algorithm.→ Greedy/DP (linear)

Interview Tip

Always check constraints first before coding. If N ≤ 20, don't waste time optimizing beyond O(2^N). If N ≥ 10⁵, don't even try O(N²) approaches.

Common Pitfalls & Traps

Even experienced developers fall into these traps. Learn to recognize them before they cost you an interview or hours of debugging.

Pitfall 1: The Greedy Trap

When Greedy Looks Right But Isn't

Coin Change Problem: Given coins [1, 3, 4], make amount 6.

Greedy (WRONG)

Pick largest first: 4 + 1 + 1 = 3 coins

DP (CORRECT)

Optimal: 3 + 3 = 2 coins

When Greedy Works vs Fails

ProblemGreedy?Why
Coin Change (arbitrary coins)NoLarger coin might not fit optimally
Coin Change (1, 5, 10, 25)YesCanonical coin system
0/1 KnapsackNoCan't take fractions
Fractional KnapsackYesCan take best value/weight ratio
Activity SelectionYesEarliest end time works provably

Pitfall 2: DP vs Backtracking Confusion

Choosing the Wrong One

Problem: "Find all subsets that sum to K"

Wrong: Using DP (it only gives count, not actual subsets)
Right: Backtracking (need to enumerate all solutions)

Problem: "Count subsets that sum to K"

Wrong: Backtracking (too slow for large N)
Right: DP (just need the count)

Quick Decision Rule

Need ALL solutions?

→ Backtracking

vs

Need COUNT or OPTIMAL?

→ DP

Pitfall 3: Forgetting to Undo in Backtracking

Wrong (No Undo)

Java
1void backtrack(List<Integer> path) {
2    if (valid(path)) result.add(path);
3
4    for (int choice : choices) {
5        path.add(choice);
6        backtrack(path);
7        // Missing: path.remove(last)!
8    }
9}
10// Bug: path keeps growing forever

Correct (With Undo)

Java
1void backtrack(List<Integer> path) {
2    if (valid(path)) {
3        result.add(new ArrayList<>(path));
4    }
5    for (int choice : choices) {
6        path.add(choice);      // Choose
7        backtrack(path);       // Explore
8        path.removeLast();     // Undo!
9    }
10}

Pitfall 4: Incomplete Memoization Key

Common Bug: Missing State in Key

Java
1// WRONG: Only memoizing on index
2int solve(int index, int remaining) {
3    if (memo[index] != -1) return memo[index];
4    // Bug: Different 'remaining' values get same cached result!
5    ...
6}
7
8// CORRECT: Include all changing state
9int solve(int index, int remaining) {
10    if (memo[index][remaining] != -1) {
11        return memo[index][remaining];
12    }
13    ...
14}

Pitfall 5: Missing Overlapping Subproblems

How to Check for Overlap

  1. Draw the recursion tree for a small input
  2. Look for repeated function calls with same arguments
  3. If you see them → DP will help
  4. If every call is unique → DP won't help (use backtracking or greedy)

Quick Reference: Avoid These Mistakes

1

Don't assume greedy works — test with counter-examples first

2

Use DP for count/optimal, Backtracking for enumerate all

3

Always undo choices in backtracking (Choose → Explore → Undo)

4

Include ALL changing state in memo key, not just the index

5

Check constraints first — they reveal the required complexity

Final Summary

Backtracking explores valid paths, DP caches overlapping work, Greedy trusts local choices, and constraints decide what's feasible.

When in doubt, start with brute force recursion, then optimize based on the problem pattern.

🎉

You've completed this article!

Ready to explore more topics?

Browse More Articles
Tags:AlgorithmsDPInterview Prep