C Mastery / Recursion, Dynamic Programming, Greedy, Backtracking
Part 8 — Data Structures and Algorithms in C

Recursion, Dynamic Programming, Greedy, Backtracking

This chapter covers the four major algorithmic paradigms: recursion, dynamic programming, greedy algorithms, and backtracking.

Why This Matters

These paradigms solve a huge fraction of algorithmic problems. Recognizing which one applies — and converting a naive recursive solution into an efficient one — is a core engineering skill.

Prerequisites

Core Concept

Recursion

Express a problem in terms of smaller instances, with a base case.

Dynamic programming (DP)

Solve overlapping subproblems once and store results (memoization or bottom-up tabulation) to avoid recomputation.

Greedy

Make the locally optimal choice at each step, hoping for a global optimum. Works only when the problem has optimal substructure and the greedy choice is provably safe.

Backtracking

Systematically explore candidate solutions, abandoning a partial solution as soon as it cannot lead to a valid one (pruning).

Examples

Fibonacci (recursion → DP)

/* Naive: O(2^n) */
unsigned long fib_rec(unsigned n)
{
    return n <= 1 ? n : fib_rec(n - 1) + fib_rec(n - 2);
}

/* DP (bottom-up): O(n), O(1) space */
unsigned long fib_dp(unsigned n)
{
    unsigned long a = 0, b = 1;
    if (n == 0) return a;
    for (unsigned i = 2; i <= n; i++) {
        unsigned long c = a + b;
        a = b; b = c;
    }
    return b;
}

Knapsack (0/1 DP)

/* dp[w] = max value for capacity w */
for (int i = 0; i < n; i++)
    for (int w = W; w >= weight[i]; w--)
        if (dp[w - weight[i]] + value[i] > dp[w])
            dp[w] = dp[w - weight[i]] + value[i];

Greedy coin change (with canonical denominations)

int coins[] = {25, 10, 5, 1};
int n = 0;
for (int i = 0; amount > 0; i++) {
    n += amount / coins[i];
    amount %= coins[i];
}

Backtracking (N-queens skeleton)

void solve(int row, int *cols, int n)
{
    if (row == n) { /* found a placement */ return; }
    for (int c = 0; c < n; c++) {
        if (is_safe(cols, row, c)) {
            cols[row] = c;
            solve(row + 1, cols, n);
        }
    }
}

How It Works

Recursion breaks a problem down; DP memoizes/tabulates overlapping results; greedy commits to local choices; backtracking searches the space with pruning.

Variations

Top-down vs. bottom-up DP

Memoization (top-down) is easy to write but uses recursion; tabulation (bottom-up) is iterative and often uses less space.

Branch and bound

Backtracking plus a bound to prune suboptimal branches (used in optimization).

Common Mistakes

Undefined Behavior

Portability

Under the Hood

Recursion uses the call stack (c.core.18). DP trades memory for time. Greedy and backtracking are control-flow patterns, not special machinery.

Practical Usage

Exercises

1. Implement Fibonacci recursively and with DP; compare runtimes. 2. Solve 0/1 knapsack with bottom-up DP. 3. Solve coin change with greedy and explain when it fails. 4. Implement N-queens with backtracking.

Deep Challenge

Implement the longest common subsequence (LCS) with DP, and recover the actual subsequence (not just its length). Explain the time/space complexity and how to reduce space to O(min(n, m)).

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.alg.recursion05
c.alg.dp06
c.alg.greedy05
c.alg.backtracking05