Content
Introduction to Recursion Tail Recursion Kinds of Recursion Iteration vs. Recursion <- Go BackUniversity of Michigan at Ann Arbor
Last Edit Date: 04/08/2023
Disclaimer and Term of Use:
We do not guarantee the accuracy and completeness of the summary content. Some of the course material may not be included, and some of the content in the summary may not be correct. You should use this file properly and legally. We are not responsible for any results from using this file
This personal note is adapted from Professor Amir Kamil, Andrew DeOrio, James Juett, Sofia Saleem, and Saquib Razak. Please contact us to delete this file if you think your rights have been violated.
This work is licensed under a Creative Commons Attribution 4.0 International License.
Recursion is the process of defining a problem (or the solution to a problem) in terms of (a simpler version of) itself.
The following example (factorial) shows the idea of recursion:
\begin{split}n! = \begin{cases} 1 &\textrm{if}~ n = 0~ \textrm{or}~ n = 1\\ n \times (n - 1) \times \cdots \times 1 &\textrm{otherwise} \end{cases}\end{split}We can implement an iterative function to compute this:
1 int factorial(int n) {
2 int result = 1;
3 while (n > 0) {
4 result *= n;
5 --n;
6 }
7 return result;
8 }
Try it out:
On the other hand, we can observe that $(n−1)! = (n−1)\times \cdots \times1$, so that we can mathematically define factorial in terms of itself:
\begin{split}n! = \begin{cases} 1 &\textrm{if}~ n = 0~ \textrm{or}~ n = 1\\ n \times (n - 1)! &\textrm{otherwise} \end{cases}\end{split}Such a mathematical definition is called a recurrence relation. It translates into code as follows:
1 int factorial(int n) {
2 if (n == 0 || n == 1) {
3 return 1;
4 } else {
5 return n * factorial(n - 1);
6 }
7 }
This function is recursive, since it calls itself as part of its definition (line 5).
Try it out:
The following picture shows how the memory is changing when calling factorial(3)
:
When the call factorial(3)
is evaluated, an activation record is created, and the parameter n
is initialized with value 3
. The body of factorial()
runs, and it calls factorial(2)
. This creates another activation record, and its parameter n
is initialized to 2
. Within the context of that activation record, the body of factorial()
is executed, and it calls factorial(1)
. Another activation record is created, with n
initialized to 1
.
The body runs, returning 1
, which replaces the function call in the caller. The activation record for factorial(1)
is destroyed, and factorial(2)
resumes. The latter computes 2 * 1
and returns 2
. The activation record for factorial(2)
is destroyed, and factorial(3)
continues where it left off. It computes 3 * 2
, returning 6
. Its activation record is reclaimed, and main()
resumes, initializing result to 6
, the correct value for $3!$.
Requirements for recursion:
A base case: can be solved without recursion
Recursive cases: break down into subproblems that are similar and smaller (closer to a base case)
A simple analogy for recursion is the Matryoshka.
The recursive code for countDolls()
looks like the following:
1 int countDolls(int n) {
2 if (n == 1) {
3 return 1;
4 }
5 else {
6 return 1 + countDolls(n - 1);
7 }
8 }
Try it out:
In its simplest form, a tactile recursive algorithm can be derived to find the smallest doll. If the doll can be opened, then the doll inside can either be opened or not. If it can’t be opened it is the smallest doll. If it can be opened, the process repeats.
The algorithm would look something like this:
Open the doll.
Can the doll inside be opened?