CS211_3_Recursion
CS211_3_Recursion
CS211
Recursion
Lectures 4 & 5
Samar Alsaleh
Spring 2022
04
03 Finishing Thoughts
Summary & resources
Agenda Introduction
Recursion motivation and general concept
Today’s 01
Agenda Introduction
Recursion motivation and general concept
What is Recursion?
“In order to understand recursion, one must first understand recursion.”
- Open a browser and type “recursion” on Google. Did you no ce the “Did you
mean: recursion” message?
- Click on that message. It will appear again. Click again. There it is again. Click it… OK, enough.
- “Recursion: the repeated applica on of a recursive procedure or de ni on.”
➔ Even recursion’s own de ni ons are recursive!
4
fi
ti
ti
ti
fi
ti
Recursion in Nature and More
5
How Does This Apply to Algorithms?
Ask
Answer
7
Recursion In Math
- Mathema cal formulas are o en expressed recursively
- N!, for any posi ve integer N, is de ned as the product of all integers between 1 and
N inclusive:
N × N−1 × N−2 × … × 1 = N × (N−1)!
8
fi
ti
ti
ti
fi
ft
fi
ti
Recursion in Programming
- Recursion is a programming technique in which a method calls itself to ful ll its purpose
- When a method invokes itself, it is called a RECURSIVE METHOD
- If the method is called, it typically divides the problem into two conceptual parts:
- a part that the method knows the solu on for it; and
- a part that it does not know how to solve it but rather how to get it reach the solvable part
- The code of a recursive method solves the problem using:
- The base case: a case for which we have a solu on
- The recursive case: a call for the recursive method but with a smaller version of the problem
- Each call sets up a new execu on environment, with new parameters and new local
variables
- As always, when the method completes, control returns to the method that invoked it
(which may be another instance of itself)
9
ti
ti
ti
fi
Recursive Method Structure
- Most recursive methods will look like:
{recursive_case
base_case → asnswer is known
recursive_alg =
→ else call me with smaller value
10
Recursive Method Structure (cont.)
- But do we really need two cases?
- Let’s try a recursive logic for “coun ng down from i” with and without the base case:
11
ti
Why Recursion?
- For many, recursion may seem hard or impossible, however, recursion o
en provides
elegant, short algorithmic solu ons to many problems in CS and mathema cs
- For some problems recursive solu ons are o en more simple and easier to express
- Usually recursive algorithms have less code, therefore algorithms can be easier to
write and understand - e.g. Towers of Hanoi
- Some mes recursion provides a much simpler solu on. Obtaining the same result
using itera on requires complicated coding - e.g. Quicksort
- Recursive methods provide a very natural mechanism for processing recursive data
structures
- A recursive data structure is a data structure that is de ned recursively – e.g. Tree
- Recursive solu
on can be much more readable and elegant than itera ve solu on to
the same problem
12
ti
ti
ti
ti
ti
ft
ti
fi
ti
ft
ti
ti
However…
- Just because we can use recursion to solve a problem, does NOT mean we should!
- You must be able to determine when recursion is the correct technique to use
- A recursive solu on may simply be less e cient
- Furthermore, recursion has the overhead of mul ple method invoca ons, and
consequently the overhead of method calls
- Each recursive call causes another copy of the method to be created
- This set of copies can consume considerable processor me and memory space (expensive)
- Since itera on occurs within a method, repeated method calls and extra memory
assignment are avoided
- So remember,
- avoid using excessively recursive algorithms even if the code is simple
- every recursive solu on has a corresponding itera ve solu on
13
ti
ti
ti
ffi
ti
ti
ti
ti
ti
Recursion vs. Itera on
14
fi
ti
Recursion vs. Itera on (cont.)
Itera on is a special case of recursion…
- Both itera on and recursion are based on a control statement:
- Itera on uses a repe on statement (e.g., for, while or do…while)
- Recursion uses a selec on statement (e.g., if, if…else or switch)
- Both itera on and recursion involve repe on:
- Itera on explicitly uses a repe on statement
- Recursion implicitly achieves repe on through repeated method calls
- Itera on and recursion each involve a termina on test:
- Itera on terminates when the loop-con nua on condi on fails
- Recursion terminates when a base case is reached
- Both itera on and recursion can occur in nitely:
- An in nite loop occurs with itera on if the loop-con nua on test never becomes false
- In nite recursion occurs if the recursion step does not reduce the problem each me in a
manner that converges on the base case, or if the base case is not tested
15
fi
ti
ti
ti
ti
fi
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
fi
ti
ti
ti
ti
ti
ti
ti
ti
Recursion vs. Itera on (cont.)
Iteration Recursion
repetition statement (e.g., for, selection statement (e.g., if, if…
Control statement
while or do…while) else or switch)
explicit through a repetition implicit through repeated
Repetion
statement method calls
16
ti
02
Agenda Introduction
Recursion motivation and general concept
Designing a Recursive Solu on
In order to design a recursive solu on, you need:
1. At least one “small” case that you can solve directly
➔ Base case
2. A way of breaking a larger problem down into one or more smaller subproblems,
each of the same kind as the original
➔ Decomposi on
3. A way of combining subproblem results into an overall solu on to the larger problem
➔ Composi on
➔ Note: All recessive solu ons require base case and decomposi on but not all require
composi on
18
ti
ti
ti
ti
ti
ti
ti
ti
Designing a Recursive Solu on (cont.)
- Let’s apply this to the problem of compu ng the factorial of a number:
‣ N! = (N − 1)! × N [for N > 1]
‣ 1! = 1
‣ 3! = 2! × 3
= (1! × 2) × 3
=1×2×3
Recursive design:
- Base case: 1!
- Decomposi on: (N − 1)!
- Composi on: × N
19
ti
ti
ti
ti
Designing a Recursive Solu on (cont.)
To verifying recursive func ons you can use the the three-ques on method:
1. Base-Case Ques on:
- Is there a non-recursive way out of the func on? i.e. What is/are the case(s) for
which you know solu on(s)?
20
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
Anatomy of a Recursive Func on
- The def func on header is similar to any other func on
- Condi onal statement check for base case
- Base cases are evaluated without recursive calls
- Recursive cases are evaluated with recursive calls
- Example, a recursive method for compu ng n!:
def factorial(n): // return the factorial of n (n!)
if n == 0: // base case
return 1
else: // recursive case
return n * factorial(n-1) // composition, decomposition
21
ti
ti
ti
ti
ti
Anatomy of a Recursive Func on (cont.)
def factorial(n): // return the factorial of n (n!)
if n == 0: // base case
return 1
else: // recursive case
return n * factorial(n-1) // composition, decomposition
23
ti
ti
ti
ti
ti
ti
ti
ft
ti
Stack Ac va on Frames
- The ac va on record stores:
- the return address for this func on call,
- the parameters,
- the local variables, and
- the func on’s return value, if non-void
- The ac va on record for a par cular func on call is
popped o the run- me stack when:
- the nal closing brace in the func on code is
reached, or
- the return statement is reached in the func on code
- At this me the func on’s return value, if non-void,
is brought back to the calling block return address
for use there
24
fi
ti
ti
ti
ti
f
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
Recursive Tracing: Example1
- Consider the following recursive method:
public static int mystery(int n) {
mystery(648):
if (n < 10) {
return n; • int a = 648 / 10; // 64
} else { • int b = 648 % 10; // 8
int a = n / 10; • return mystery(a + b); // mystery(72)
int b = n % 10;
return mystery(a + b); mystery(72):
} • int a = 72 / 10; // 7
} • int b = 72 % 10; // 2
• return mystery(a + b); // mystery(9)
- What is the result of the following call? mystery(9):
mystery(648) • return 9;
25
Recursive Tracing: Example2
- Tracing the recursive calls of the factorial(3) on the run- me stack
26
ti
More Recursive Methods - sum()
- Consider the problem of compu ng the sum of all N N−1
∑ ∑
the integers between 1 and N, inclusive: i=N+
i=1 i=1
- If N is 5, the sum = 1 + 2 + 3 + 4 + 5 N−2
∑
- This problem can be expressed recursively as: = N + N−1 +
The sum of 1 to N = N + the sum of 1 to N − 1 i=1
N−3
- A recursive method that computes the sum of N: = N + N−1 + N−2 +
∑
. i=1
.
public int sum(int num) { .
if (num == 0) //base case = N + N−1 + N−2 + . . . + 2 + 1
return num;
return num + sum(num-1); //general case
}
27
ti
More Recursive Methods - sum() (cont.)
- Tracing the recursive calls of the sum method:
10 6
4 + sum(3)
main
3
3 + sum(2)
sum
sum(4) 1
2 + sum(1)
sum
sum(3)
1
sum
sum(2)
sum
sum(1)
28
More Recursive Methods - power()
- From mathema cs, we know that
0
‣ 2 =1 and 25 = 2 * 24
- In general,
0
‣ x =1 x n = x * x n−1
and
for integer x, and integer n > 0
Power
Power(2,0)
30
03
Agenda Introduction
Recursion motivation and general concept
Types of Recursion
A recursive method is characterized based on:
1. Whether the method calls itself or not
- Direct vs. Indirect
2. Whether the recursion is nested or not
- Nested vs. Non-Nested
3. Whether there are pending opera ons at each recursive call
- Tail vs. Non-tail
4. Whether pending opera ons are also recursive (the shape of the calling pa ern)
- Linear(Single) vs. Tree(Mul ple)
5. Whether the method is excessively recursive or not
32
ti
ti
ti
tt
1. Direct vs. Indirect Recursion
- A method invoking itself is considered to be
direct recursion
- A method could invoke another method,
which invokes another, etc., un l eventually
the original method is invoked again
- For example, method m1 could invoke m2,
which invokes m3, which invokes m1 again
- This is called indirect recursion
- It is o en more di cult to trace and debug
33
ft
ffi
ti
1. Direct vs. Indirect Recursion (cont.)
- If a func on F calls itself (i.e F calls F), then this recursion is Direct Recursion
static int Direct(int n){
if (n<=0)
return 0;
return n + Direct(n-1);
}
- If a func on F calls G and func on G calls F, then this recursion is Indirect Recursion
static int InDirect(int n){
if (n<=0)
return 0;
return n + Buddy(n-1);
}
35
ti
ti
ti
fi
ti
ti
3. Tail vs. Non-tail Recursion
- In tail recursion, a single recursive call is the
last statement to be executed in the func on
- a method is tail recursive if in each of its
recursive cases it executes one recursive call
and if there are no pending opera ons a er
that call
- In non-tail (or head) recursion, the recursive
call is not the last statement to be executed
in the func on
- Tail recursion can be replaced by itera
on to
remove recursion from the solu on as in the
next examples
36
ti
ti
ti
ft
ti
ti
3. Tail vs. Non-tail Recursion (cont.)
- It is easy to convert a tail recursive method into an itera ve one:
Tail Recursive Method Corresponding Iterative Method
public static void f1(int n) {
public static void f1(int n) {
System.out.print(n + " ");
for(int k = n; k >= 0; k--)
if (n > 0)
System.out.print(k + " ");
f1(n - 1);
}
}
ti
4. Linear vs. Tree Recursion
- Recursion that only contains a single self-
reference is known as linear or single recursion long factorial (int n) {
if (n == 0)
- E.g.: Factorial func on return 1;
- In the factorial func on, each invocaon of else
return n * factorial (n–1);
factorial() makes at most one new recursive call
}
- Recursion that contains mul ple self-
references is known as tree or mul ple
recursion int fib(int n){
if (n == 0 || n == 1)
- E.g.: Fibonacci func on return n;
- In the Fibonacci func on, every recursive call else
return fib(n–1) + fib(n–2);
has a pending opera on that involves another
}
recursive call
38
ti
ti
ti
ti
ti
ti
ti
ti
5. Excessive Recursion
- A recursive method is excessively recursive if it repeats computa ons for some
parameter values
- Example: The call fib(6) results in two repe
ons of fib(4). This in turn results in
repe ons of fib(3), fib(2), fib(1) and fib(0):
40
ti
ti
fi
tt
ti
ti
Common Errors in Wri ng Recursive Methods ….……… …
There are some mistakes you should pay a en on to while wri ng recursive methods:
1. The method does not call itself directly or indirectly se
a
2. Non-termina ng Recursive Methods (In nite recursion): rd a b
wa
- No base case: s o
t ect!
e sse orr
int badFactorial(int x) {
og r i nc
return x * badFactorial(x-1);
l pr n is
} cal rsio
ive ecu
c u rs e r
- The base case is never reached
r h parameter values:
e for, tsome
ac h ise
t e erw x) {
int anotherBadFactorial(int
a
if(x == 0) e th O th
s
returnur1; ase.
else B
e c
return x*(x-1)*anotherBadFactorial(x-2);
// When x is odd, we never reach the base case!!
}
41
ti
ti
fi
tt
ti
ti
Common Errors in Wri ng Recursive Methods (cont.). …
3. Post increment and decrement operators must not be used since the update will not
occur un l AFTER the method call - (in nite recursion)
public static int sumArray (int[ ] x, int index) {
if (index == x.length)
return 0;
else
return x[index] + sumArray (x, index++);
}
4. Local variables must not be used to accumulate the result of a recursive method.
Each recursive call has its own copy of local variables
public static int sumArray (int[ ] x, int index) {
int sum = 0;
if (index == x.length)
return sum;
else {
sum += x[index];
return sumArray(x, index+1);
}
}
42
ti
ti
fi
Common Errors in Wri ng Recursive Methods (cont.). …
5. Wrong placement of return statement
- Consider the following method that is supposed to calculate the sum of the rst n integers:
public static int sum (int n, int result) {
if (n >= 0)
sum(n-1, n+result);
return result;
}
- When result is ini alized to 0, the method returns 0 for whatever value of the
parameter n
- The result returned is that of the nal return statement to be executed
- Example, a trace of the call sum(3,0) is:
sum(3,0) sum(2,3) sum(1,5) sum(0,6) sum(-1,6)
6 6 6 6
44
ti
Common Errors in Wri ng Recursive Methods (cont.). …
6. The use of instance or sta c variables in recursive methods should be avoided
- Although it is not an error, it is bad programming prac ce
- These variables may be modi ed by code outside the method and cause the recursive
method to return wrong result
public class Sum{
private int sum;
public int sumArray(int[] x, int index){
if(index == x.length)
return sum;
else {
sum += x[index];
return sumArray(x, index+1);
}
}
}
45
ti
fi
ti
ti
Analyzing Recursive Algorithms
- To determine the order of a loop, we determine the order of the body of the loop
mul plied by the number of loop execu ons
- Similarly, to determine the order of a recursive method, we determine the order of
the body of the method mul plied by the number of mes the recursive method is
called
- Recursion complexity = Number of recursive calls × Body complexity
- Example, using a recursive solu on to compute the sum of integers from 1 to n, the
recursive method is invoked n mes and the method itself is O(1). So the order of the
overall solu on is O(n)
46
ti
ti
ti
ti
ti
ti
ti
Analyzing Recursive Algorithms (cont.)
Recursion Methods:
- Analyze from the inside (or deepest part) rst and work outwards
- Find the number of recursive calls and the body complexity.
- Example:
long factorial (int n) {
if (n <= 1)
• Number of recursive calls: n − 1
return 1;
else
return n * factorial (n – 1);
➡ • Body complexity: O(1)
Complexity of factorial(): O(n)
}
1 1 n−1
47
fi
When to Use a Recursive Solu on?
Shallow Depth:
- The depth of recursive calls is rela vely “shallow” compared to the size of the
problem
E ciency:
- The recursive version does about the same amount of work as the non-recursive
version. However, it may cause a higher overhead
Clarity:
- The recursive version is shorter and simpler than the non-recursive solu on
48
ffi
ti
ti
ti
04
03 Finishing Thoughts
Summary & resources
Agenda Introduction
Recursion motivation and general concept
Summary
Other Resources:
- https://courses.cs.washington.edu›lectures›LectureA ›09-recursion
- Java How to Program, 9/e- Chapter 18, Recursion
- https://slideplayer.com/slide/5043474/
53
Thank You & Stay Safe
:)