0% found this document useful (0 votes)
15 views35 pages

2 Recursion

Uploaded by

abdulkarimmawji
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views35 pages

2 Recursion

Uploaded by

abdulkarimmawji
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 35

Recursion

Dr. Hakim Mellah


Department of Computer Science & Software Engineering
Concordia University, Montreal, Canada

These slides has been extracted, modified and updated from original slides of :
• Data Structures and Algorithms in Java, 5th edition. John Wiley& Sons, 2010. ISBN 978-0-470-38326-1.
• Dr. Hanna’s slides (http://aimanhanna.com/concordia/comp352/index.htm)

Copyright © 2010 Michael T. Goodrich, Roberto Tamassia


All rights reserved
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 Java method:
// recursive factorial function
public static int recursiveFactorial(int n) {
if (n == 0) return 1; // base case
else return n * recursiveFactorial(n- 1); // recursive case
}
Recursion 2
Content of a Recursive Method
❑ Base case(s)
◼ Also referred to as stopping cases. These are the
cases where the method performs NO more
recursive calls.
◼ 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 method itself.
◼ Each recursive call should be defined so that it
makes progress towards a base case.
Recursion 3
Visualizing Recursion
❑ Example
❑ Recursion trace
return 4*6 = 24 final answer
A box for each
call

recursive call
recursiveFactorial (4)

call return 3*2 = 6


◼ An arrow from each
recursiveFactorial (3)
caller to callee call return 2*1 = 2
◼ An arrow from each recursiveFactorial (2)
callee to caller showing call return 1*1 = 1
return value recursiveFactorial (1)

➔ See Recursion1.java call return 1

Recursion2.java recursiveFactorial (0)

Recursion 4
Recursion & The Stack
❑ A running Java program maintains a private memory
area called the stack, which is used to keep track of
the methods as they are invoked.

❑ Whenever a method is invoked, its information


(parameters, local variables, Program Counter (PC),
…) is placed as one frame into the stack.

❑ The frame is removed from the stack once the method


returns.

Recursion 5
Recursion & The Stack
main()
fun5():
{…
PC = 328
fun1(); m=2
… n=5
} fun1():
Frames PC = 229
fun1() y=7
{…
fun5();
main():

PC = 24
} Java Stack x = 10
Recursion 6
Recursion & The Stack
❑ The heap is another memory area that is maintained
for a running program.

❑ The heap is used for dynamic allocation of memory at


runtime (i.e. when new is called to create an object).

❑ Usually the stack and the heap grow against each


other in the memory.

❑ Recursion has hence the potential of overflowing the


stack by quickly consuming all available space.
❑ ➔ See Recursion3.java
Recursion 7
Linear Recursion
❑ Simplest form of recursion, where the method makes at
most one recursive call each time it is invoked.

❑ Very useful when the problem is viewed in terms of first or


last element, plus a remaining set that has the same
structure as the original set.

❑ For instance, obtaining the summation of n values in an


array can be viewed as:
◼ Obtaining the sum of the first n-1 elements plus the value of the
last element;
◼ If the array has only one element, then the summation is that
single value, A[0] .
Recursion 8
Example of Linear Recursion
Algorithm LinearSum(A, n): Example recursion trace:
Input:
An integer array A and an call return 15 + A[4] = 15 + 5 = 20
integer n >= 1, such that A LinearSum (A,5)
has at least 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]
A
4 3 6 2 5

Recursion 9
Example: 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

Recursion 10
Defining Arguments for Recursion
❑ In creating recursive methods, it is important
to define the methods in ways that facilitate
recursion.

❑ This sometimes requires additional


parameters to be passed to the method.

❑ For example, we defined the array reversal


method as ReverseArray(A, i, j), not
ReverseArray(A).
Recursion 11
Example: 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 a power function that runs in


O(n) time (for we make n recursive calls).

❑ However, can we do better than this?


Recursion 12
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
 p ( x , n / 2) 2
if x  0 is even

❑ 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.

Recursion 13
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
Recursion 14
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.
Recursion 15
Tail Recursion
❑ Tail recursion occurs when a linearly recursive
method makes its recursive call as its last step; as in
the array reversal method.

❑ 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 ] ➔See Factorial.java
i =i+1
j =j-1
return
Recursion 16
Binary Recursion
❑ Binary recursion occurs whenever there are two,
and exactly two, recursive calls for each non-
base case.

❑ Applicable, for instance, when attempting to


solve two different halves of some problem.

❑ Example: Calculating the summation of an n


array elements, can be done by:
◼ Recursively summing the elements in the first half;
◼ Recursively summing the elements in the second half;
◼ Adding the two values.
Recursion 17
Example: Summing Array Elements
Example: Summing n consecutive elements of an array,
starting from a given index i, using binary recursion
Algorithm binarySum(A, i, n)
Input An array A and integers i and n
Output The sum of the n element of 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 )

Recursion 18
Example: Summing Array Elements
❑ The following provides an example of a
binarySum(0, 8) trace, where each box indicates the
starting index and the number of elements to sum.

❑ Analysis: In every half, the call will be made n-1


times, resulting in a total of 2n - 1 calls ➔ O(n).
❑ However, it should be noted that there is a maximum
of 1 + log2 n active calls at any point of time, which
improves space utilization as we discuss later.
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
19
Recursion
Example: Fibonacci Numbers
❑ In mathematics, the Fibonacci numbers are the
numbers in the following integer sequence:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...

❑ By definition, the first two Fibonacci numbers are 0


and 1, and each subsequent number is the sum of
the previous two.

❑ Fibonacci numbers can be defined recursively as:


F0 = 0
F1 = 1
Fi = Fi-1 + Fi-2 for i > 1.
Recursion 20
Example: Fibonacci Numbers
❑ 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)

Recursion 21
Example: Fibonacci Numbers
❑ Analysis: Let nk be the number of recursive calls
(notice that this is not the value) 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.
❑ In fact, nk > 2k/2. It is exponential!
Recursion 22
Example: Fibonacci Numbers
❑ The main problem with binaryFib(k) approach is that
the computation of Fibonacci numbers is really a
linearly recursive problem, in spite of its look where Fk
depends on Fk-1 and Fk-2.

❑ The problem is hence not a good candidate for binary


recursion.

❑ We should use linear recursion instead.

Recursion 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)

// notice that the values are retuned (however, not both are
displayed)
❑ linearFibonacci makes k-1 recursive calls, so total calls is k.
➔ See LinearFib.java & BinaryFibStack.java

Recursion 24
A Better Fibonacci Algorithm
For instance (note: Fib is short for linearFibonacci),
❑ Fib(2) ➔ (i+j, i) is (1,1) ➔ Will be displaying 1
❑ Fib(3) ➔ (i+j, i) is (2,1) ➔ Will be displaying 2

❑ Fib(4) ➔ (i+j, i) is (3,2) ➔ Will be displaying 3

❑ Fib(5) ➔ (i+j, i) is (5,3) ➔ Will be displaying 5


❑ Fib(6) ➔ (i+j, i) is (8,5) ➔ Will be displaying 8

❑ Fib(7) ➔ (i+j, i) is (13,8) ➔ Will be displaying 13


❑ Fib(8) ➔ (i+j, i) is (21,13) ➔ Will be displaying 21

❑ Fib(9) ➔ (i+j, i) is (34,21) ➔ Will be displaying 34

❑ :
❑ Fib(12) ➔ (i+j, i) is (144,89) ➔ Will be displaying
144
Recursion 25
Binary Recursion
Another Example
❑ The English Ruler:
◼ Print the ticks and numbers like an English ruler

Recursion 26
Slide by Matt Stallmann included with permission.

Example: The English Ruler


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 )

Recursion 27
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)

◼ A single tick of length L drawOneTick (1)

drawTicks (0)
◼ An interval with a central drawOneTick (3)
tick length L−1 drawTicks (2)
(previous pattern repeats )

Recursion 28
Java Implementation (1)
// draw ruler
public static 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
}
}
Note the two
// draw ticks of given length recursive calls
public static 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
}
}
Recursion 29
Java Implementation (2)
// draw a tick with no label
public static void drawOneTick(int tickLength) {
drawOneTick(tickLength, - 1); // -1 will avoid printing the label
}

// 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");
}

Recursion 30
Multiple Recursion
❑ Multiple recursion:
◼ makes potentially many recursive calls
◼ not just one or two
❑ Motivating example:
◼ Coping folders (directories)
◼ summation puzzles
 pot + pan = bib
 dog + cat = pig
 boy + girl = baby

Recursion 31
Example of Multiple Recursion
Algorithm CopyFolder(folder):
Input: A directory folder, which possibly includes files and subfolders
Output: A copy of the given folder with all its files and subfolders

for all files in folder do


copy file
for all subfolder in folder do
copyfolder(subfolder) // this line is where recursion happens

Recursion 32
Example of 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
Recursion 33
Slide by Matt Stallmann
included with permission.

Example of Multiple Recursion


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

Recursion 34
Visualizing PuzzleSolve
❑ Notice that the number of concurrently active calls can
still be limited with multiple recursion.
❑ For instance, the number of active calls of CopyFolder
depends on how many nested subfolders may exist at
a time and not on the total number of subfolders in
the directory. 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 Recursion bca cba 35

You might also like