Data Structures
Static Data Structures
Recursion and their applications
5 November 2024 1
Recursion (3.5)
⚫ In some problems, it may be natural to define
the problem in terms of the problem itself.
⚫ Recursion is useful for problems that can be
represented by a simpler version of the same
problem.
⚫ Example: the factorial function
6! = 6 * 5 * 4 * 3 * 2 * 1
We could write:
6! = 6 * 5!
2
Recursion (cont.)
⚫ Recursion is one way to decompose a task into
smaller subtasks. At least one of the subtasks is
a smaller example of the same task.
⚫ The smallest example of the same task has a
non-recursive solution.
⚫ Example: the factorial function
n! = n*(n-1)! and 1! = 1
3
Example: Factorial Function
⚫ In general, we can express the factorial
function as follows:
n! = n*(n-1)!
Is this correct? Well… almost.
⚫ The factorial function is only defined for
positive integers. So we should be more
precise:
f(n) = 1 if n = 1
= n*f(n-1) if n > 1
4
Factorial Function: Pseudo-code
int fac(int n){
if(n == 0)
return 1;
else
return n * fac(n-1);
}
recursion means that a function calls itself.
5
Visualizing Recursion
Example recursion trace:
Recursion trace
return 4*6 = 24 final answer
⚫ A box for each call
recursiveFactorial(4)
recursive call call return 3*2 = 6
⚫ An arrow from each recursiveFactorial(3)
call return 2*1 = 2
caller to callee recursiveFactorial(2)
⚫ An arrow from each call return 1*1 = 1
recursiveFactorial(1)
callee to caller call return 1
showing return value recursiveFactorial(0)
6
factorial function
The C++ equivalent of this definition:
int fac(int n){
if (n<=1)
return 1;
else
return n * fac(n-1);
}
recursion means that a function calls
itself
Recursive vs. Iterative Solutions
⚫ For certain problems (such as the factorial function), a
recursive solution often leads to short and elegant code.
Compare the recursive solution with the iterative solution:
int fac(int n){ int fac(int n){
if (n==0) int product = 1;
return 1; while(n>1){
else product *= n;
return (n * fac(n-1)); n--;
} }
return product;
}
8
A Word of Caution
⚫ To trace recursion, function calls operate as a stack –
the new function is put on top of the caller.
⚫ We have to pay a price for recursion:
calling a function consumes more time and
memory than adjusting a loop counter.
high performance applications (graphic action
games, simulations of nuclear explosions) hardly
ever use recursion.
⚫ In less demanding applications, recursion is an
attractive alternative for iteration (for the right
problems!)
9
Infinite Loops
If we use iteration, we must be careful not to create an
infinite loop by accident.
for (int incr=1; incr!=10; incr+=2)
...
int result = 1; Oops!
while(result > 0){
...
result++;
} Oops!
10
Infinite Recursion
Similarly, if we use recursion, we must be careful not
to create an infinite chain of function calls.
int fac(int n){ Oops!
return n * fac(n-1); No termination
} condition
int fac(int n){
if (n==0)
return 1;
else
return n * fac(n + 1);
}
Oops! 11
Tips
We must always make sure that the recursion
bottoms out:
⚫ A recursive function must contain at least one
non-recursive branch.
⚫ The recursive calls must eventually lead to a
non-recursive branch.
12
Direct Computation Method
⚫Fibonacci numbers:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
where each number is the sum of the
preceding two.
⚫Recursive definition:
F(0) = 0;
F(1) = 1;
F(number) = F(number-1)+ F(number-
2);
Example 2: Fibonacci numbers
//Calculate Fibonacci numbers using recursive function.
//A very inefficient way, but illustrates recursion well
int fib(int number)
{
if (number == 0) return 0;
if (number == 1) return 1;
return (fib(number-1) + fib(number-2));
}
int main(){ // driver function
int n;
cout << "Please enter an integer: ";
cin >> n;
cout << "The Fibonacci number for "<< n << " is “ << fib(n) << endl;
return 0;
}
Example 3: Fibonacci number w/o recursion
//Calculate Fibonacci numbers iteratively
//much more efficient than recursive solution
int fib(int n)
{
int f[100];
f[0] = 0; f[1] = 1;
for (int i=2; i<= n; i++)
f[i] = f[i-1] + f[i-2];
return f[n];
}