DATA STRUCTURE & ALGORITHMS
RECURSION (part 1)
Waris Ali
NUML, Islamabad
What is Recursion?
• Recursion: comes from re occurance
• In Mathematics: repetition of steps to give
result
• In Programming: Recursion occurs when a
method calls itself to solve a simpler version of
the problem. With each recursive call, the
problem is different from, and simpler than, the
original problem.
3/1/2024 2
What is Recursion?
• Recursion involves the internal use of a stack.
• A stack is a data abstraction that works like
this: New data is "pushed," or added to the
top of the stack. When information is removed
from the stack it is "popped," or removed from
the top of the stack.
• The recursive calls of a method will be stored
on a stack, and manipulated in a similar
manner.
3/1/2024 3
Recursively Defined Sequences
• Often it is difficult to express the members of an
object or numerical sequence explicitly.
• EG: The Fibonacci sequence:
• {fn } = 0,1,1,2,3,5,8,13,21,34,55,…
• There may, however, be some “local” connections
that can give rise to a recursive definition –a
formula that expresses higher terms in the
sequence, in terms of lower terms.
• EG: Recursive definition for {fn }:
INITIALIZATION: f0 = 0, f1 = 1
RECURSION: fn = fn-1+fn-2 for n > 1.
L16 4
3/1/2024 4
Recursive Functions
It is possible to think of any function with domain
N as a sequence of numbers, and vice-versa.
Simply set: fn =f (n)
For example, our Fibonacci sequence becomes
the Fibonacci function as follows:
f (0) = 0, f (1) = 1, f (2) = 1, f (3) = 2,…
Such functions can then be defined recursively by
using recursive sequence definition. EG:
INITIALIZATION: f (0) = 0, f (1) = 1
RECURSION: f (n)=f (n -1)+f (n -2), for n > 1.
L16 5
3/1/2024 5
Content of a Recursive Method
• Base case(s).
• Values of the input variables for which we perform no recursive
calls are called base cases (there should be at least one base
case).
• Every possible chain of recursive calls must eventually reach a
base case.
• The base case is a fundamental situation where no further
problem solving is necessary.
• In the case of finding factorials, the answer of 0! is by definition
== 1. No further work is needed.
• Recursive calls.
• Calls to the current method.
• Each recursive call should be defined so that it makes progress
towards a base case.
3/1/2024 Department of IT, NUML Islamabad, Autumn 2011 6
Recursion Example – Solving Factorials
• The factorial operation in mathematics is illustrated below.
0! = 1
1! = 1 * 1 or 1 * 0!
2! = 2 * 1 or 2 * 1!
3! = 3 * 2 * 1 or 3 * 2!
4! = 4 * 3 * 2 *1 or 4 * 3!
Notice that each successive line can be solved in terms of
the previous line. For example, 4! is equivalent to the
problem
4 * 3!
3/1/2024 7
Recursion Example – Solving Factorials
• Classic example: Here is the non-recursive definition of the factorial
function:
• n! = 1· 2· 3· ··· · (n-1)· n
• Here is the recursive definition of a factorial:
(here f(n) = n!)
1 if n 0
f ( n)
n f (n 1) else
• Code of recursive procedures, in functional programming languages
like Java, is almost identical to a recursive definition!
3/1/2024 8
Using A Recursive Method
• A recursive method to solve the factorial problem is given below.
Notice in the last line of the method the recursive call.
• The method calls another implementation of itself to solve a smaller
version of the problem.
int fact(int n)
// returns the value of n!
// precondition: n >= 0
{
if (n == 1 || n == 0)
return 1;
else
return n * fact(n - 1);
}
The base case is a fundamental situation where no further problem
solving is necessary.
In the case of finding factorials, the answer of 0! & 1! is by definition
== 1. No further work is needed.
Recursion – Step By Step
• Suppose we call the method to solve fact(4).
• This will result in four calls of method fact.
fact(4): This is not the base case (n==1) so we return the result of 4 *
fact(3). This multiplication will not be carried out until an answer is
found for fact(3). This leads to the second call of fact to solve
fact(3).
fact(3): Again, this is not the base case and we return
3 * fact (2). This leads to another recursive call to solve fact(2).
fact(2): Still, this is not the base case, we solve 2 * fact(1).
fact(1): Finally we reach the base case, which returns the value 1.
Recursion – Step By Step
• When a recursive call is made, the current computation is
temporarily suspended and placed on the stack with all its
current information available for later use.
• A completely new copy of the method is used to evaluate
the recursive call.
• When that is completed, the value returned by the
recursive call is used to complete the suspended
computation.
• The suspended computation is removed from the stack
and its work now proceeds.
Recursion – Step By Step
• When the base case is encountered the recursion will now
unwind and result in a final answer.
• The expressions below should be read from right to left.
fact(4) = 4 * fact(3) = 3 * fact(2) = 2 * fact(1) = 1
24 4 * 6 3 * 2 2 * 1
Recursion – The Picture
• Here is a picture. Look at what happens:
fact (4) 24
•Each box represents a call of
method fact. To solve fact(4)
requires four calls of method
fact (4)
fact.
4 * fact (3) 6
•Notice that when the
fact (3)
recursive calls were made
inside the else statement, the
3 * fact (2) 2 value fed to the recursive call
was (n-1). This is where the
problem is getting smaller and
fact (2)
simpler with the eventual goal
2 * fact (1) 1 of solving 1!.
fact (1)
Pitfalls Of Recursion
• If the recursion never reaches the base case, the recursive
calls will continue until the computer runs out of memory
and the program crashes.
• The message “stack overflow error” or “heap storage
exhaustion” indicates a possible runaway recursion.
• When programming recursively, you need to make sure
that the algorithm is moving toward the base case.
• Each successive call of the algorithm must be solving a
simpler version of the problem.
• Any recursive algorithm can be implemented iteratively,
but sometimes only with great difficulty.
• However, a recursive solution will always run more slowly
than an iterative one because of the overhead of opening
and closing the recursive calls.
Recursion Examples
• On the next few slides, you will see some
examples of how recursion is used in
programming.
• You should take out a sheet of paper and
a pencil and try to work each of them out
by hand.
Recursion – Example #2
int result = identity(10); returned
identity(num)
System.out.println("The final answer is " + value
result); identity (0) 10
public int identity(int num) identity (2) 12
{ identity (4) 16
if (num < 1) identity (6) 22
return 10; identity (8) 30
else identity (10) 40
return num + identity(num
- 2);
}
The final answer is 40.
Recursion - Example #3
returned
int result2 = negative(-3); negative(num) value
System.out.println("The final negative(21) -5
answer is " + result2); negative(17) 29
public int negative(int num) negative(13) 55
negative(9) 73
{
negative(5) 83
if (num >= 20)
negative(1) 85
return -5; negative(-3) 79
else
return negative(num +
4) + 2 * num;
}
The final answer is 79.
Recursion – Example #4
product(num) returned value
int result3 = product(1);
product(64) -1
System.out.println("The final answer32is " +
product(-32)
result3); product(16) 512
public int product(int num) product(-8) -4096
{ product(4) -16384
if (num > 20) product(-2) 32768
product(1) 32768
return -1;
else
return num * product(-2 * num);
}
The final answer is 32768.
DATA STRUCTURE & ALGORITHMS
RECURSION (part 2)
Waris Ali
NUML, Islamabad
Linear Recursion
• Test for base cases.
• Begin by testing for a set of base cases (there should
be at least one).
• Every possible chain of recursive calls must
eventually reach a base case, and the handling of
each base case should not use recursion.
• Recur once.
• Perform a single recursive call at each step . (This
recursive step may involve a test that decides which
of several possible recursive calls to make, but it
should ultimately choose to make just one of these
calls each time we perform this step.)
• Define each possible recursive call so that it makes
progress towards a base case.
3/1/2024 20
A Simple Example of Linear Recursion
Algorithm LinearSum(A, n):
Input:
An integer array A and an Example recursion trace:
integer n = 1, such that A has at
least n elements
call return 15 + A [4] = 15 + 5 = 20
Output:
LinearSum (A ,5)
The sum of the first n integers in
call return 13 + A[3] = 13 + 2 = 15
A
LinearSum (A ,4)
if n = 1 then
call return 7 + A [2] = 7 + 6 = 13
return A[0]
LinearSum (A ,3)
else
call return 4 + A [1] = 4 + 3 = 7
return LinearSum(A, n - 1) + A[n LinearSum (A,2)
- 1]
call return A[0] = 4
LinearSum (A,1)
3/1/2024 21
Reversing an Array
Algorithm ReverseArray(A, i, j):
Input: An array A and nonnegative integer indices i and j
Output: The reversal of the elements in A starting at index i and
ending at j
if i < j then
Swap A[i] and A[ j]
ReverseArray(A, i + 1, j - 1)
return
3/1/2024 22
Defining Arguments for Recursion
• In creating recursive methods, it is important to define the methods in
ways that facilitate recursion.
• This sometimes requires we define additional parameters that are
passed to the method.
• For example, we defined the array reversal method as
ReverseArray(A, i, j), not ReverseArray(A).
3/1/2024 23
Computing Powers
• The power function, p(x,n)=xn, can be defined recursively:
• This leads to an power function that runs in O(n) time (for we make n
recursive calls).
1 if n 0
p ( x, n )
x p( x, n 1) else
• We can do better than this, however.
3/1/2024 24
Tail Recursion
• Tail recursion occurs when a linearly recursive method
makes its last recursive call as its last step.
• The array reversal method is an example.
• Such methods can be easily converted to non-
recursive methods (which saves on some resources).
• Example:
Algorithm IterativeReverseArray(A, i, j ):
Input: An array A and nonnegative integer indices i and j
Output: The reversal of the elements in A starting at index i
and ending at j
while i < j do
Swap A[i ] and A[ j ]
i =i+1
j =j-1
return
3/1/2024 25
Tail Recursion Examples
• Every linear recursion can be converted to tail
recursion
• Example:
int f=1;
int fact(int n)
// returns the value of n!
// precondition: n >= 0
{
if (n == 1 || n == 0)
return f;
else
f=f*n;
fact(n - 1);
3/1/2024 26
Tail Recursion Examples
f =1
call
Factorial (4)
call f= 1*4 = 4
recursiveFactorial (3 )
call f= 4 *3 = 112
recursiveFactorial (2 )
call f= 12*2 = 24
recursiveFactorial (1 )
return 24 final answer
3/1/2024 27
BINARY-SEARCH
Alg.: BINARY-SEARCH (A, lo, hi, x)
if (lo > hi)
return FALSE
mid (lo+hi)/2
if x = A[mid]
return TRUE
if ( x < A[mid] )
BINARY-SEARCH (A, lo, mid-1, x)
if ( x > A[mid] )
BINARY-SEARCH (A, mid+1, hi, x)
Consider the below given array & draw recursion trees for
searching 11 & 8 using binary search
1 2 3 4 5 7 9 11 28
Non Linear Recursion
• Test for base cases.
• Begin by testing for a set of base cases (there should
be at least one).
• Every possible chain of recursive calls must
eventually reach a base case, and the handling of
each base case should not use recursion.
• Recur more than once.
• Perform more than one recursive call at each step
• Define each possible recursive call so that it makes
progress towards a base case.
3/1/2024 29
Fibonacci Series
Every element is the sum of its predecessors: 1, 1, 2, 3, 5, 8, 13, 21…
Recursive algorithm
fib(0) = 0
fib(1) = 1
fib(n) = fib(n-1) + fib(n-2) :n > 1
int fib(int n)
{
if (n == 1 || n == 0)
return n;
else
return fib(n-1)+fib(n-2);
30
31
Recursion Tree
Example 6
MergeSort(L)
1) if (length of L > 1) {
2) Split list into first half and second half
3) MergeSort(first half)
4) MergeSort(second half)
5) Merge first half and second half into sorted list
}
3/1/2024 32