Recursion
Section 3.5
© 2010 Goodrich, Tamassia Programming with Recursion 1
The Recursion Pattern
❑ Recursion: when a method calls itself
❑ Classic example: the factorial function:
n! = 1· 2· 3· ··· · (n−1)· n
❑ Recursive definition:
1 if n = 0
f ( n) =
n f (n − 1) else
❑ As a C++method:
// recursive factorial function
int recursiveFactorial(int n) {
if (n == 0) return 1; // basis case
else return n * recursiveFactorial(n- 1); // recursive case
}
© 2010 Goodrich, Tamassia Programming with Recursion 2
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.
❑ Recursive calls
◼ Calls to the current method.
◼ Each recursive call should be defined so that it
makes progress towards a base case.
© 2010 Goodrich, Tamassia Programming with Recursion 3
Visualizing Recursion
❑ Recursion trace ❑ Example
◼ A box for each call
return 4*6 = 24 final answer
recursive call recursiveFactorial (4)
◼ An arrow from each call return 3*2 = 6
caller to callee recursiveFactorial (3)
◼ An arrow from each call return 2*1 = 2
callee to caller recursiveFactorial (2)
showing return value call return 1*1 = 1
recursiveFactorial (1)
call return 1
recursiveFactorial (0)
© 2010 Goodrich, Tamassia Programming with Recursion 4
Example: English Ruler
❑ Print the ticks and numbers like an English ruler:
© 2010 Goodrich, Tamassia Programming with Recursion 5
Slide by Matt Stallmann
included with permission.
Using Recursion
drawTicks(length)
Input: length of a ‘tick’
Output: ruler with tick of the given length in
the middle and smaller rulers on either side
drawTicks(length)
if( length > 0 ) then
drawTicks( length − 1 )
draw tick of the given length
drawTicks( length − 1 )
© 2010 Stallmann Programming with Recursion 6
Recursive Drawing Method
❑ The drawing method is drawTicks (3) Output
based on the following drawTicks (2)
recursive definition
drawTicks (1)
drawTicks (0)
❑ An interval with a drawOneTick (1)
central tick length L >1 drawTicks (0)
consists of: drawOneTick (2)
◼ An interval with a central drawTicks (1)
tick length L−1 drawTicks (0)
◼ An single tick of length L drawOneTick (1)
An interval with a central
drawTicks (0)
◼ drawOneTick (3)
tick length L−1 drawTicks (2)
(previous pattern repeats )
© 2010 Goodrich, Tamassia Programming with Recursion 7
C++ Implementation (1)
// draw ruler
void drawRuler(int nInches, int majorLength) {
drawOneTick(majorLength, 0); // draw tick 0 and its label
for (int i = 1; i <= nInches; i++) {
drawTicks(majorLength- 1); // draw ticks for this inch
drawOneTick(majorLength, i); // draw tick i and its label
}
}
// draw ticks of given length
void drawTicks(int tickLength) {
if (tickLength > 0) { // stop when length drops to 0
drawTicks(tickLength- 1); // recursively draw left ticks
drawOneTick(tickLength); // draw center tick
drawTicks(tickLength- 1); // recursively draw right ticks
}
}
© 2010 Goodrich, Tamassia Programming with Recursion 8
C++ Implementation (2)
// draw a tick with no label
void drawOneTick(int tickLength) {
drawOneTick(tickLength, - 1);
}
// draw one tick
void drawOneTick(int tickLength, int tickLabel) {
for (int i = 0; i < tickLength; i++)
cout << "-";
if (tickLabel >= 0) cout << " " << tickLabel;
cout << "\n";
}
© 2010 Goodrich, Tamassia Programming with Recursion 9
Recursion Examples
Example 3.2 in the text: Programming languages are often defined
in a recursive way. We can define an argument list in C++ as
follows:
argument-list:
ε
argument
argument-list , argument
That is, an argument list consists of either (i) the empty string, (ii)
an argument, or (iii) an argument list followed by a comma and
an argument.
foo();
bar(14);
bletch(23.1, ‘a’, 14);
© 2019 Shermer Programming with Recursion 10
Example of Linear Recursion
Algorithm LinearSum(A, n): Example recursion trace:
Input:
A integer array A and an integer call return 15 + A[4] = 15 + 5 = 20
n = 1, such that A has at least LinearSum(A,5)
n elements call return 13 + A[3] = 13 + 2 = 15
Output: LinearSum (A,4)
The sum of the first n integers call return 7 + A[2] = 7 + 6 = 13
in A LinearSum (A,3)
if n = 1 then call return 4 + A[1] = 4 + 3 = 7
return A[0] LinearSum (A,2)
else call return A[0] = 4
return LinearSum(A, n - 1) + LinearSum (A,1)
A[n - 1]
© 2010 Goodrich,
Using Recursion 11
Tamassia
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
Using Recursion © 2010 Goodrich, Tamassia 12
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
paramaters that are passed to the method.
❑ For example, we defined the array reversal
method as ReverseArray(A, i, j), not
ReverseArray(A).
Using Recursion © 2010 Goodrich, Tamassia 13
Computing Powers
❑ The power function, p(x,n)=xn, can be
defined recursively:
1 if n = 0
p( x, n) =
x p( x, n − 1) else
❑ This leads to an power function that runs in
O(n) time (for we make n recursive calls).
❑ We can do better than this, however.
14 © 2010 Goodrich, Tamassia Using Recursion
Recursive Squaring
❑ We can derive a more efficient linearly
recursive algorithm by using repeated squaring:
1 if x = 0
p( x, n) = x p( x, (n − 1) / 2) 2 if x 0 is odd
2
if x 0 is even
p ( x , n / 2)
❑ For example,
24 = 2(4/2)2 = (24/2)2 = (22)2 = 42 = 16
25 = 21+(4/2)2 = 2(24/2)2 = 2(22)2 = 2(42) = 32
26 = 2(6/ 2)2 = (26/2)2 = (23)2 = 82 = 64
27 = 21+(6/2)2 = 2(26/2)2 = 2(23)2 = 2(82) = 128.
15 © 2010 Goodrich, Tamassia Using Recursion
Recursive Squaring Method
Algorithm Power(x, n):
Input: A number x and integer n = 0
Output: The value xn
if n = 0 then
return 1
if n is odd then
y = Power(x, (n - 1)/ 2)
return x · y ·y
else
y = Power(x, n/ 2)
return y · y
Using Recursion © 2010 Goodrich, Tamassia 16
Analysis
Algorithm Power(x, n):
Input: A number x and
integer n = 0 Each time we make a
Output: The value xn recursive call we halve
the value of n; hence,
if n = 0 then we make log n recursive
return 1 calls. That is, this
if n is odd then method runs in O(log n)
y = Power(x, (n - 1)/ 2) time.
return x · y · y
else It is important that we
use a variable twice
y = Power(x, n/ 2) here rather than calling
return y · y the method twice.
Using Recursion © 2010 Goodrich, Tamassia 17
Tail Recursion
❑ Tail recursion occurs when a linearly recursive
method makes its 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
Using Recursion © 2010 Goodrich, Tamassia 18
Binary Recursion
❑ Binary recursion occurs whenever there are two
recursive calls for each non-base case.
❑ Example: the DrawTicks method for drawing
ticks on an English ruler.
Using Recursion © 2010 Goodrich, Tamassia 19
A Binary Recursive Method for
Drawing Ticks
// draw a tick with no label
public static void drawOneTick(int tickLength) { drawOneTick(tickLength, - 1); }
// draw one tick
public static void drawOneTick(int tickLength, int tickLabel) {
for (int i = 0; i < tickLength; i++)
System.out.print("-");
if (tickLabel >= 0) System.out.print(" " + tickLabel);
System.out.print("\n");
Note the two
} recursive calls
public static void drawTicks(int tickLength) { // draw ticks of given length
if (tickLength > 0) { // stop when length drops to 0
drawTicks(tickLength- 1); // recursively draw left ticks
drawOneTick(tickLength); // draw center tick
drawTicks(tickLength- 1); // recursively draw right ticks
}
}
public static void drawRuler(int nInches, int majorLength) { // draw ruler
drawOneTick(majorLength, 0); // draw tick 0 and its label
for (int i = 1; i <= nInches; i++) {
drawTicks(majorLength- 1); // draw ticks for this inch
drawOneTick(majorLength, i); // draw tick i and its label
}
} Recursion
Using © 2010 Goodrich, Tamassia 20
Another Binary Recusive Method
❑ Problem: add all the numbers in an integer array A:
Algorithm BinarySum(A, i, n):
Input: An array A and integers i and n
Output: The sum of the n integers in A starting at index i
if n = 1 then
return A[i ]
return BinarySum(A, i, n/ 2) + BinarySum(A, i + n/ 2, n/ 2)
❑ Example trace:
0, 8
0, 4 4, 4
0, 2 2, 2 4, 2 6, 2
0, 1 1, 1 2, 1 3, 1 4, 1 5, 1 6, 1 7, 1
21 © 2010 Goodrich, Tamassia Using Recursion
Computing Fibonacci Numbers
❑ Fibonacci numbers are defined recursively:
F0 = 0
F1 = 1
Fi = Fi-1 + Fi-2 for i > 1.
❑ Recursive algorithm (first attempt):
Algorithm BinaryFib(k):
Input: Nonnegative integer k
Output: The kth Fibonacci number Fk
if k = 1 then
return k
else
return BinaryFib(k - 1) + BinaryFib(k - 2)
Using Recursion © 2010 Goodrich, Tamassia 22
Analysis
❑ Let nk be the number of recursive calls by BinaryFib(k)
◼ n0 = 1
◼ n1 = 1
◼ n2 = n1 + n0 + 1 = 1 + 1 + 1 = 3
◼ n3 = n2 + n1 + 1 = 3 + 1 + 1 = 5
◼ n4 = n3 + n2 + 1 = 5 + 3 + 1 = 9
◼ n5 = n4 + n3 + 1 = 9 + 5 + 1 = 15
◼ n6 = n5 + n4 + 1 = 15 + 9 + 1 = 25
◼ n7 = n6 + n5 + 1 = 25 + 15 + 1 = 41
◼ n8 = n7 + n6 + 1 = 41 + 25 + 1 = 67.
❑ Note that nk at least doubles every other time
❑ That is, nk > 2k/2. It is exponential!
Using Recursion © 2010 Goodrich, Tamassia 23
A Better Fibonacci Algorithm
❑ Use linear recursion instead
Algorithm LinearFibonacci(k):
Input: A nonnegative integer k
Output: Pair of Fibonacci numbers (Fk , Fk−1)
if k = 1 then
return (k, 0)
else
(i, j) = LinearFibonacci(k − 1)
return (i +j, i)
❑ LinearFibonacci makes k−1 recursive calls
Using Recursion © 2010 Goodrich, Tamassia 24
Multiple Recursion
❑ Motivating example:
◼ summation puzzles
pot + pan = bib
dog + cat = pig
boy + girl = baby
❑ Multiple recursion:
◼ makes potentially many recursive calls
◼ not just one or two
Using Recursion © 2010 Goodrich, Tamassia 25
Algorithm for Multiple Recursion
Algorithm PuzzleSolve(k,S,U):
Input: Integer k, sequence S, and set U (universe of elements to
test)
Output: Enumeration of all k-length extensions to S using elements
in U without repetitions
for all e in U do
Remove e from U {e is now being used}
Add e to the end of S
if k = 1 then
Test whether S is a configuration that solves the puzzle
if S solves the puzzle then
return “Solution found: ” S
else
PuzzleSolve(k - 1, S,U)
Add e back to U {e is now unused}
Remove e from the end of S
Using Recursion © 2010 Goodrich, Tamassia 26
Slide by Matt Stallmann
included with permission.
Example
cbb + ba = abc a,b,c stand for 7,8,9; not
799 + 98 = 997 necessarily in that order
[] {a,b,c}
[a] {b,c} [b] {a,c} [c] {a,b}
a=7 b=7 c=7
[ab] {c} [ac] {b} [ca] {b} [cb] {a}
a=7,b=8 a=7,c=8 c=7,a=8 c=7,b=8
c=9 b=9 b=9 a=9
[ba] {c} [bc] {a}
b=7,a=8 b=7,c=8 might be able to
c=9 a=9 stop sooner
Using Recursion © 2010 Stallmann 27
Visualizing PuzzleSolve
Initial call
PuzzleSolve (3,(),{a,b,c})
PuzzleSolve (2,a,{b,c}) PuzzleSolve (2,b,{a,c}) PuzzleSolve (2,c,{a,b})
PuzzleSolve (1,ab,{c}) PuzzleSolve (1,ba,{c}) PuzzleSolve (1,ca,{b})
abc bac cab
PuzzleSolve (1,ac,{b}) PuzzleSolve (1,bc,{a}) PuzzleSolve (1,cb,{a})
acb bca cba
Using Recursion © 2010 Goodrich, Tamassia 28