Week4 Recursion
Week4 Recursion
▪ Recursive Solution
Recursion in Action: factorial(n)
6
Finding a recursive solution
• Each successive recursive call should bring you closer to a
situation in which the answer is known, e.g. in the previous
slide computing (n-1)! helps you get closer to computing n!
• Binary recursion occurs whenever there are two recursive calls for
each non-base case.
Example: fibonacci(n) calls fibonacci(n-1) and fibonacci(n-2)
Questions:
• To calculate factorial(5), how many times is the function factorial
invoked (called)?
• To calculate fibonacci(5), how many times is the function fibonacci
invoked?
Linear and Binary Recursion
Question 1:
To calculate factorial(5), how many times is the function factorial
invoked (called)?
int factorial ( int n ) {
if ( n == 0) // base case
return 1;
else // general/recursive case
return n * factorial ( n – 1);
}
int fibonacci(int n) {
if (n <=1)
return n;
return fibonacci(n-1) + fibonacci(n-2);
}
Total calls = 15
Recursion: run-time analysis
• Time complexity of recursion is proportional to the problem size
– Normally, it is equal to the number of times the function
calls itself
– Check the general case!
i j
5 10 18 30 45 50 60 65 70 80
• Update i and j in such a way that they converge to the base case (i = j)
Linear recursion: Reversing an Array
Reversing an Array
17
What happens when a function is called?
• The rest of the execution in “caller” is
suspended int a(int w)
{
return w+w;
• An activation record is created on stack, }
containing
int b(int x)
– Return address (in the caller code) {
int z,y=1;
– Current (suspended) status of the caller z = a(x) + y;
return z;
• Control is transferred to the “called” function }
18
What happens when a recursive function is called?
Except the fact that the calling and called functions have the same name,
there is really no difference between recursive and non-recursive calls.
Tail Recursion
20
Tail Recursion
Key Characteristics:
1. Last Operation: In a tail-recursive function, the recursive
call is the last operation, and its result is immediately
returned. There are no pending operations after the
recursive call.
2. Optimization: Tail recursion can often be optimized by
compilers into an iterative form, eliminating the need to
create new stack frames for each recursive call.
21
Tail Recursion
• Example: Tail-Recursive Factorial
• Consider the tail-recursive implementation of the factorial calculation
function in C++:
Illustration:
int factorial(int n, int result = 1) { •When the factorial function is
called with n and result as
if (n == 0) {
arguments, it multiplies n with
return result; // Base case the current result and
} else { decrements n in each recursive
// Tail-recursive call call.
return factorial(n - 1, n * result); •The base case is reached when
n becomes 0, and at that point,
}
the result is returned as the
} factorial value.
22
Tail Recursion
Advantages of Tail Recursion:
• Efficient memory usage: Tail-recursive functions can be optimized
to avoid creating new stack frames, which can save memory and
prevent stack overflow errors for large inputs.
Notes:
• Not all recursive functions can be easily converted to tail recursion.
The recursive call must be the last operation, and no additional
work should be done after the recursive call for it to be optimized.
• Tail recursion is an important concept, especially when working
with functional programming languages and optimizing recursive
algorithms for performance.
23
Binary Search using Binary Recursion
A is an array, key is the element to be found, LO is initialized as 0,
and HI is initialized as array size - 1
int BinarySearch(int key, int A[], int LO, int HI){
if (LO > HI)then // key does not exist
return -1; Complexity in worst case:
int mid = (LO+HI)/2; O( log2 n)
Best case:
if (key == A[mid]) // base case O(1)
return mid;
else if (key < A[mid]) // recursive case I
BinarySearch(key, A, LO, mid - 1);
else // recursive case
II
BinarySearch(key, A, mid + 1, HI);
}
Efficiency of recursion
25
Recursion: general remarks
26
Examples on Recursion with Linked Lists (1)
Write a recursive function to print all data fields in a Singly Linked List.
void printList(Node* ptr){
if(ptr==NULL) //base case
return;
cout << ptr->data << endl;
printList(ptr->next);
}
27
Examples on Recursion with Linked Lists (2)
Write a recursive function to add all data fields in a Singly Linked List.
int AddNodes(Node* ptr){
if(ptr==NULL) //base case
return 0;
return ptr->data + AddNodes(ptr->next);
}
28
Examples on Recursion with Linked Lists (3)
29
Mathematical Analysis of
Recursive Algorithms
30
Mathematical Analysis of Recursive
Algorithms
31
Mathematical Analysis of Recursive
Algorithms: Towers of Hanoi
• Have n disks of different sizes that can slide onto any of three
pegs
• Initially, all the disks are on the first peg in order of size, the
largest on the bottom and the smallest on top
• The goal is to move all the disks to the third peg, using the
second one as an auxiliary, if necessary
• We can move only one disk at a time, and it is forbidden to
place a larger disk on top of a smaller one
32
Mathematical Analysis of Recursive
Algorithms: Towers of Hanoi
33
Mathematical Analysis of Recursive
Algorithms: Towers of Hanoi
• We first move recursively n − 1 disks from peg
1 to peg 2 (with peg 3 as auxiliary)
• Then move the largest disk directly from peg 1
to peg 3
• Finally, move recursively n − 1 disks from peg 2
to peg 3 (using peg 1 as auxiliary).
• if n = 1, move the single disk directly from the
source peg to the destination peg.
34
Mathematical Analysis of Recursive
Algorithms: Towers of Hanoi
35
Mathematical Analysis of Recursive
Algorithms: Towers of Hanoi
36