Lecture 21: Recursion

Mar 27, 2024  β”‚  Last updated Mar 24, 2024 by Charlotte Curtis

HTML Slides html β”‚ PDF Slides PDF

Where we left off

  • friend functions and stream operators
  • A common abstract data type: stacks
  • Designing a SetInt ADT

Textbook Sections 11.2, 13.2

class StringStack {
public:
...
private:
    struct Node {
        std::string data;
        Node *next;
    };
    Node *head;
    int capacity;
    int size;
};

Today’s topics

Textbook Section 13.2, Chapter 14

And now, recursion!

The call stack

bg right:30% 50%

int f() {
    int x = g();
    return x;
}

int g() {
    int x = h();
    return x;
}

int h() ...
int main() {
    int result = f();
    return 0;
}

Functions calling themselves

bg right:30% 50%

int f() {
    int x = f();
    return x;
}
int main() {
    int result = f();
    return 0;
}

Definition of Recursion

Divide and conquer

Example: Factorial

Tracing recursive functions

int factorial(int n) {
    if (n == 0)
        return 1;
    else
        return n * factorial(n-1);
}

int main() {
    cout << factorial(4) << endl;
}

Thinking recursively, step by step

  1. What is the base case? This is the simplest case that must be solved directly.
    • For the factorial example, this is factorial(0) = 1
    • There may be more than one base case!
  2. What is the recursive case? This is the case that depends on a prior case.
    • For the factorial example, this is factorial(n) = n * factorial(n-1)
    • There may be more than one recursive case!
  3. How does the recursive case get closer to the base case?
    • For the factorial example, this is n-1
    • This is referred to as the reduction step

Typical structure of a recursive function

if (base case)
    solve the problem
else
    reduce the problem
    call the function again

The Towers of Hanoi

bg right:40% fit

Recursion involving Linked Lists

Example: computing the length of a linked list

Printing a linked list

Given a list of 0 -> 1 -> 2 -> 3 -> NULL, trace the following:

Iterative solution

void print(Node *head) {
    while (head) {
        cout << head->data << endl;
        head = head->next;
    }
}

Recursive solution

void print(Node *head) {
    if (head) {
        cout << head->data << endl;
        print(head->next);
    }
}

Reversing the order of actions

Given a list of 0 -> 1 -> 2 -> 3 -> NULL, trace the following:

void print(Node *head) {
    if (head) {
        print(head->next);
        cout << head->data << endl;
    }
}

Why wouldn’t we use recursion?

Tail recursion

g++ may optimize other forms of recursion, but it’s not guaranteed

Tail recursion example

Back to the linked list deletion example:

void clear_list(Node *head) {
    if (head) {
        clear_list(head->next);
        delete head;
    }
}

emoji Recursion check-in 1/2

Can any recursive function be implemented iteratively?

  1. Yes
  2. No

emoji Recursion check-in 2/2

Trace the following code and write the result:

int mystery(int n) {
    if (n < 2)
        return n;
    else
        return mystery(n-1) + mystery(n-2);
}

int main() {
    cout << mystery(4) << endl;
}

Recursion with arrays

Searching an array

Binary search with recursion

Coming up next



Previous: Lecture 20: ADT Case study
Next: Lecture 22: Copying Objects