Post - 2030 L4

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 39

2020-09-23

Advanced Object Oriented


Programming

EECS 2030
Lecture 4 :: Recursion

Think recursively

1
2020-09-23

Think
recursively

“how many people are there,


from you to the end of line?”

“ 1 + the answer of the guy


behind me“

General Definition of Recursion


• Recursion: The definition of an operation in terms of
itself.
▪ Solving a problem using recursion depends on solving
smaller occurrences of the same problem.

• Recursion solution: algorithm that finds the solution to


a given problem by reducing the problem to smaller
versions of itself.

2
2020-09-23

Recursion
• Recursion is the process of defining a problem (or the solution to a
problem) in terms of (a simpler version of) itself.
▪ number of characters in a string is 1 + number characters in the
remaining string
o lengh("ABCD") = 1 + length("BCD")
▪ a × b can be solved by a + a × (b-1)
▪ ab can be solved by a × ab-1
▪ sum of n numbers is sum of first and rest
o sum(4,8,7,5) = 4 + sum(8,7,5)

▪ Max value in a list is the larger of first and max of rest list
▪ Factorial n can be defined/solved as n! = n * (n-1)!
▪ Fibonacci sequence is defined as: F(i) = F(i-1) + F(i-2)
5

Why recursion?
• Solves some problems more naturally than
iteration
▪ In computer science, some problems are more easily
solved by using recursive functions.
o Tower of Hanoi

• Leads to elegant, simplistic, short code (when


used well)
▪ More efficient?

3
2020-09-23

Recursive Definitions
• Every recursive algorithm involves at least 2 cases:
▪ Base case(s).
▪ Recursive calls/cases.

• Anchor, ground, or base case:


▪ Case in recursive definition in which the solution is obtained
directly
▪ Stops the recursion If your instance is sufficiently
small, solve it yourself as a base
case.

• General case:
▪ Case in recursive definition in which a smaller version of itself
is called
▪ Usually by changing the parameter(s).
▪ Must eventually be reduced to a base case
o Progress toward base case

Recursive Algorithm: smaller instance


• Assume you have an algorithm that works.
• Use it to write an algorithm that works.

To get into my house


I must get the key from a smaller house
Assume I made it.
8

4
2020-09-23

Recursion

“To get into my house


I must get the key from a smaller house

getIn (house A) // Java


{
getIn (smaller house on the right)
pick up key for A
open A;
}

Recursive Algorithm: base cases


• Assume you have an algorithm that works.
• Use it to write an algorithm that works.

Use brute force


to get into
the smallest house.

10

5
2020-09-23

Recursion

“To get into my house


I must get the key from a smaller house

getIn (house A) // Java


{
if (A is the last/smallest)
get in using brute force;

else
getIn (house on the right)
pick up key for A
open A;
}
11

11

Recursion
“To get into my house
I must get the key from a smaller house

int length (string s) // Java


{ if (s contains no letter)
return 0;
else
int restL = length(substring on the right);
return 1 + restL;
}

length(“ABCD”)
= 1 + length(“BCD”)
= 1 + ( 1 + length(“CD”))
= 1 + ( 1 + ( 1 + length(“D”))) Can save restL
=121 + ( 1 + ( 1 + (1+ (1+length(“”) )))
=1+ (1+ ( 1 + (1+ (1+0) ))) = 4
12

6
2020-09-23

Recursion
“To get into my house
I must get the key from a smaller house

int length (string s) // Java


{ if (s contains no letter)
return 0;
return 1 + length(substring on the right);
}

length(“ABCD”)
= 1 + length(“BCD”)
= 1 + ( 1 + length(“CD”))
= 1 + ( 1 + ( 1 + length(“D”)))
= 1 + ( 1 + ( 1 + (1+ length(“”) )))
=13 1 + ( 1 + ( 1 + (1+ 0 ))) = 4

13

Recursion
“To get into my house
I must get the key from a smaller house

static int length (String s)


{ if (s.equals("") // contains no letter
return 0;
return 1 + length(s.substring(1));
}

length(“ABCD”)
= 1 + length(“BCD”)
= 1 + ( 1 + length(“CD”))
= 1 + ( 1 + ( 1 + length(“D”)))
= 1 + ( 1 + ( 1 + (1+ length(“”) )))
=14 1 + ( 1 + ( 1 + (1+ 0 ))) = 4

14

7
2020-09-23

Recursive Methods
• Caller “pause” on method call

• Every recursive call has its own


▪ Code
▪ Set of parameters
▪ Set of local variables

• After completing a recursive call


▪ Control goes back to the calling environment
▪ Recursive call must execute completely before control
goes back to previous call
▪ Execution in previous call resumes from point
immediately following recursive call
15

15

static int length ("ABCD")


{
if ("ABCD".equals("")
return 0;
return 1 + length("ABCD".substring(1));
} static int length ("BCD")
{
if ("BCD".equals("")
return 0;
3 return 1 + length("BCD".substring(1));
4 }

2
int a = length("ABCD") static int length ("CD")
{
System.out.println(a); if ("CD".equals("")
return 0;
return 1 + length("CD".substring(1));
}

static int length ("D")


static int length ("")
1
{
{ if ("D".equals("")
if ("".equals("") return 0;
return 0; 0 return 1 + length("D".substring(1));
return 1 + length(s.substring(1)); }
}

16

16

8
2020-09-23

Recursion - example1
• Think/define iteratively

static int factorial (int n)


{
int fac = 1;
for(int i=1; i<=n;i++)
fac *= i;
return fac; factorial 5 = 1 * 2 * 3 * 4 * 5 = 120
}

17

17

Recursion
• Think/define recursively `

 1 if n = 0
factorial (n) = 
n  factorial (n − 1) otherwise

static int factorial (int n)


{
if(n == 0) /* base case */
return 1;
else
return n * factorial (n – 1);
18
}
18

9
2020-09-23

Visualizing Factorial
Recursion trace tree for factorial(4) :
return 4 * 6 = 24 final answer
call

factorial (4 )

call return 3 *2 = 6

factorial (3 )

call return 2 *1 = 2

factorial (2 )

call return 1 *1 = 1

factorial (1 )

call return 1

factorial (0 )

19

static int factorial (4)


{
Program trace, more details
if(4 == 0) /* base case */ static int factorial (3)
return 1; {
return 4 * factorial (4 – 1); if(3 == 0) /* base case */
} return 1;
6
return 3 * factorial (3 – 1);
}

24 2
static int factorial (2)
{
int a = factorial(4) if(2 == 0) /* base case */
return 1;
return 2 * factorial (2 – 1);
}

static int factorial (1) 1


{
n=0 return 1
if(1 == 0) /* base case */
n=1 return ? LN = 3 return 1;
return 1 * factorial (1 – 1);
n=2 return ? LN = 3 }

n=3 return ? LN = 3 1
static int factorial (0)
n=4 return ? LN = 3 {
if(0 == 0) /* base case */
call/execution/program stack return 1;
return n * factorial (0 – 1);
}
20

10
2020-09-23

int a = factorial(4)

100 main method

a factorial(4)

main()

21 call/execution/program stack

21

int a = factorial(4)

100 main method

a factorial(4)

600 factorial method


fact(4)
n 4
main()
result 4 * factorial(3)

22 call/execution/program stack

22

11
2020-09-23

int a = factorial(4)

100 main method

a factorial(4)

750 factorial method

n 3

result 3 * factorial(2)

fact(3) 600 factorial method


fact(4)
n 4
main()
result 4 * factorial(3)

23 call/execution/program stack

23

int a = factorial(4)

100 main method

a factorial(4)
800 factorial method

n 2

result 2 * factorial(1)

750 factorial method

n 3

result 3 * factorial(2)

fact(2)
fact(3) 600 factorial method
fact(4)
n 4
main()
result 4 * factorial(3)

24 call/execution/program stack

24

12
2020-09-23

950 factorial method


int a = factorial(4) n 1

result 1 * factorial(0)

100 main method

a factorial(4)
800 factorial method

n 2

result 2 * factorial(1)

750 factorial method

n 3

result 3 * factorial(2)
fact(1)
fact(2)
fact(3) 600 factorial method
fact(4)
n 4
main()
result 4 * factorial(3)

25 call/execution/program stack

25

950 factorial method


int a = factorial(4) n 1

result 1 * factorial(0)

100 main method

a factorial(4)
800 factorial method

n 2

result 2 * factorial(1)

750 factorial method

n 3

fact(0) result 3 * factorial(2)


fact(1)
fact(2)
fact(3) 600 factorial method
fact(4)
n 4
main()
result 4 * factorial(3)

26 call/execution/program stack

26

13
2020-09-23

950 factorial method


int a = factorial(4) n 1

result 1 * 1

100 main method

a factorial(4)
800 factorial method

n 2

result 2 * factorial(1)

750 factorial method

n 3

result 3 * factorial(2)
fact(1)
fact(2)
fact(3) 600 factorial method
fact(4)
n 4
main()
result 4 * factorial(3)

27 call/execution/program stack

27

int a = factorial(4)

100 main method

a factorial(4)
800 factorial method

n 2

result 2 * 1

750 factorial method

n 3

result 3 * factorial(2)

fact(2)
fact(3) 600 factorial method
fact(4)
n 4
main()
result 4 * factorial(3)

28 call/execution/program stack

28

14
2020-09-23

int a = factorial(4)

100 main method

a factorial(4)

750 factorial method

n 3

result 3 * 2

fact(3) 600 factorial method


fact(4)
n 4
main()
result 4 * factorial(3)

29 call/execution/program stack

29

int a = factorial(4)

100 main method

a factorial(4)

600 factorial method


fact(4)
n 4
main()
result 4 * 6

30 call/execution/program stack

30

15
2020-09-23

int a = factorial(4)

100 main method

a 24

main()

31 call/execution/program stack

31

Recursion example 2
• Think/define non-recursively b4 = b * b * b * b

//iterative solution to calculate pow(base, n)


public static int power(int base, int n)
{
// initialize result by 1
int pow = 1;

// multiply x exactly n times


for (int i = 0; i < n; i++) {
pow = pow * base;
}
return pow; power (2, 3) = 2 * 2 * 2 = 8
}

•32 Think/define recursively? b4 = b* b3

32

16
2020-09-23

Recursion
• Think/define recursively `
“To get into my house
I must get the key from a smaller house
bn = b* bn-1

 1 if n = 0
power (base, n) = 
base  power (base, n − 1) otherwise

int power (int base, int n) // assume n >= 0


{
if(n == 0) /* base case */
return 1;
power(4, 3)
else = 4 * power(4, 2)
= 4 * 4 * power(4, 1)
return base * power (base, n–1); = 4 * 4 * 4 * power(4,0)
33 = 4 * 4 * 4 * 1
} = 64

33

static int power (2, 4)


{ static int power (2, 3)
if(4 == 0) /* base case */ {
return 1; if(3 == 0) /* base case */
else return 1;
return 2 * power (2, 4 – 1); else
} return 2 * power (2, 3 – 1);

8 }
4
16 static int power (2, 2)
{
if(2 == 0) /* base case */
int a = power(2, 4)
return 1;
else
return 2 * power (2, 2 – 1);
}
2
static int power (2, 1)
{
base=2 n=0 return 1
if(n == 0) /* base case */
return 1;
base=2 n=1 return ? LN = 4
else
base=2 n=2 return ? LN = 4 return 2 * power (2, 1 – 1);
}
base=2 n=3 return ? LN = 4
static int power (2, 0)
base=2 n=4 return ? LN = 4 1
{
if(0 == 0) /* base case */
call/execution/program stack return 1;
else
return n * power (2, 0 – 1);
34

17
2020-09-23

Recursion
• Think/define recursively `
“To get into my house
I must get the key from a smaller house

 1 if n = 0
power (base, n) = 
base  power (base, n − 1) otherwise

int power (int base, int n) // assume n >= 0


{
if(n == 1) /* base case */
return base;
power(4, 3)
else = 4 * power(4, 2)
= 4 * 4 * power(4, 1)
return base * power (base, n–1); = 4 * 4 * 4
35
= 64
} Save one recursive call, but…

35

Recursion
• Think/define recursively `
“To get into my house
Anther way to reduce to smaller/sub-problem I must get the key from a smaller house

1 𝑖𝑓 𝑛 𝑖𝑠 0
𝑛 𝑛 if 𝑛 = 0
𝑝𝑜𝑤𝑒𝑟(𝑏𝑎𝑠𝑒, 𝑛) = ቊ 𝑝𝑜𝑤𝑒𝑟(𝑏𝑎𝑠𝑒, ) ⋅ 𝑝𝑜𝑤𝑒𝑟(𝑏𝑎𝑠𝑒, 2 ) 𝑖𝑓 𝑛 𝑖𝑠 𝑒𝑣𝑒𝑛
𝑝𝑜𝑤𝑒𝑟(𝑏𝑎𝑠𝑒, 𝑛/2)2 ⋅ 𝑝𝑜𝑤𝑒𝑟(𝑏𝑎𝑠𝑒, 𝑛/2)
𝑛−1 𝑛−1
𝑏𝑎𝑠𝑒 ⋅ 𝑝𝑜𝑤𝑒𝑟 𝑏𝑎𝑠𝑒, ⋅ 𝑝𝑜𝑤𝑒𝑟 𝑏𝑎𝑠𝑒, 𝑖𝑓 𝑛 𝑖𝑠 𝑜𝑑𝑑
2 2
int power (int base, int n) // assume n >= 0
{ 2 4 = 2 2 * 22
if(n == 0) /* base case */ 25 = 2 * 22 * 22
return 1;
else{
if (n%2==0) { // n is even
int powHalf = power (base, n/2);
return powHalf * powHalf; } return power (base, n/2)*
power (base, n/2);
else { // n is odd
int powHalf = power (base, (n-1)/2);
36
return base * powHalf * powHalf;}}

36

18
2020-09-23

static int power (2, 4) static int power (2, 2)


{ {
if(4 == 0) /* base case */ if(2 == 0) /* base case */
return 1; return 1;
else else
if (n%2==0){ if (n%2==0){
int powHalf = power (2, 4/2); int powHalf = power (2, 2/2);
return powHalf * powHalf; return powHalf * powHalf;
else 4 else
int powHalf = power (2, (4-1)/2); int powHalf = power (2, (4-1)/2);
return 2 * powHalf * powHalf; return 2 * powHalf * powHalf;
} }

static int power (2, 1)


16 2
{
int a = power(2, 4) if(1 == 0) /* base case */
return 1;
else
if (n%2==0){
int powHalf = power (2, 1/2);
return powHalf * powHalf;
base=2 n=0 return 1 else
int powHalf = power (2, (1-1)/2);
base=2 n=1 return ? LN = 8 return 2 * powHalf * powHalf;
}
base=2 n=2 return ? LN = 5 1
static int power (2, 0)
base=2 n=4 return ? LN = 5 {
if(0 == 0) /* base case */
call/execution/program stack return 1;
else

37

Example 3a - Number of Zeros in an int array


• Example: 2 0 3 0 0 has 3 zeros. 2 0 0 3 1 has 2 zeros
• Think iteratively: scan the array, counting zeros
• Think recursively?
▪ If first element is zero: 1 + number zeros in the rest array
▪ If first element is non-zero: number of zeros in the rest array
▪ Base case?
▪ How to pass “rest array” (smaller version of problem)?
▪ subarray, or, original array with sliding index

/* Counts the number of zeros in an integer n */


countZeros(int[] a, int index) : // start from 0
if (index >= a.length) //pass array boundary
return 0;
else
if (a[index] == 0) // count it!
return 1 + countZeros(a, index+1);
else
38
return 0 + countZeros(a, index+1);

38

19
2020-09-23

countZeros(int[] a, int index) : // start from 0


if (index == a.length) //pass array boundary
return 0;
else
if (a[index] == 0)
return 1 + countZeros(a, index+1);
else
return 0 + countZeros(a, index +1);

int[] a = {2,0,3,0}
return 0 +2 = 2 final answer
countZeros(a, 0); call

countZeros (a,0)

call return 1 +1 = 2

countZeros (a,1)

call return 0 +1 = 1

countZeros (a,2)

Similar problems: call return 1+0 = 1


• # of even/odd value
countZeros (a,3)
if (a[index] %2 == 0) …
• # of values > 5 call return 0

if (a[index] > 5) … countZeros (a,4)


• 39Max/min value

39

Example 3b - Number of Zeros in a Number


Harder to make smaller problem

• Example: 20300 has 3 zeros 20031 has 2 zeros


• If n is a single digit, trivial (base case)
• If n has two or more digits
▪ Array approach? 2~~0300 or 2030~~0
▪ 2030 and 0. the number of zeros is the number of
zeros in n with the last digit removed
o plus an additional 1 if the last digit is zero

• Examples:
▪ number of zeros in 20300 is number of zeros in 2030
plus 1
▪ number of zeros in 20031 is number of zeros in 2003

40

40

20
2020-09-23

Recursive Methods & Return Values


• Pseudo code:

/**
* Counts the number of zeros in an integer n
*/
countZeros(n) :

if the last digit in n is a zero


return 1 + countZeros(n with last digit removed)

else // last digit is non-zero


return 0 + countZeros(n with last digit removed)

How to check last digit? n%10 319%10 = 9 310%10 = 0


How to get 2003 (last digit removed?) n/10 319/10=31
41

41

Recursive Methods & Return Values


• Pseudo code:

/**
* Counts the number of zeros in an integer n
*/
countZeros(n) :

if (n%10 ==0) // the last digit in n is a zero


return 1 + countZeros(n / 10)

else
return countZeros(n / 10)

How to check last digit? n%10 319%10 = 9 310%10 = 0


How to get 2003 (last digit removed?) n/10 319/10=31
42

42

21
2020-09-23

Always start by …
• Establishing the base case(s) !!
▪ what is the smallest version(s) of the problem??
▪ when should the recursion stop?

▪ In this example: when you reach a single digit (not zero


digits; you never reach zero digits!)

o base case #1 : n == 0
return 1

o base case #2 : n != 0 && n < 10


return 0

43

43

numberOfZeros method
public static int numberOfZeros(int n)
{
int zeroCount;
Boolean lastDigitZero = (n%10==0);
base cases, n <10
if (n==0) - always first – if hit, will
zeroCount = 1; force return.. stops
recursion
else if (n < 10) // 1~9
zeroCount = 0; // no zeros MUST BE EXHAUSTIVE
(cover every case that
has a solution)
else if (lastDigitZero)
zeroCount = 1 + numberOfZeros(n/10);
else // n%10 != 0
zeroCount = 0 + numberOfZeros(n/10);

44 return zeroCount;
}
44

22
2020-09-23

numberOfZeros method
public static int numberOfZeros(int n)
{
if (n==0)
return 1;
else if (n < 10) // and not 0
return 0; // 0 for no zeros

if (n%10 == 0)
return 1 + numberOfZeros(n/10);
else
return numberOfZeros(n/10);

}
Shorter version
45

45

public static int numberOfZeros(int n)


{
if (n==0)
Execution Trace return 1;
else if (n < 10)
return 0;
else if (n%10 == 0)
return 1 + numberOfZeros(n/10);
else // n%10 != 0
return 0 + numberOfZeros(n/10);
}
return 0 +2 = 2 final answer
call

numberOfZeros (20301)

call return 1 +1 = 2

numberOfZeros ( 2030 )

call return 0 +1 = 1

countZeros (203)

call return 1+0 = 1

countZeros (20)

call return 0

countZeros (2 )

46

23
2020-09-23

Example 4

Printing n of Something
• suppose you want to implement a method that prints out
n copies of a string, one copy per line

public static void printIt(String s, int n) {

for(int i = 0; i < n; i++) { printIt("Hi", 3)


System.out.println(s); Hi
} Hi
} Hi

printIt("*", 4)
*
*
*
47 *

47

A Different (recursive) Solution


We can use the following algorithm:
▪ if n == 0 done
▪ otherwise:
1. print the string once
2. print the string (n – 1) more times -- recursion

public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
else {
System.out.println(s);
printItToo(s, n - 1); // method invokes itself
}
}

48

48

24
2020-09-23

Recursion
public static void printItToo(String s, int n) {
• a method that calls
if (n itself
== 0) is
{ called a recursive method
return;
• a recursive method
} solves a problem by repeatedly
reducing the problem
else { so that a base case can be
System.out.println(s);
reached printItToo(s, n - 1); // method invokes itself
}
}

printItToo("*", 5)
* printItToo ("*", 4)
* printItToo ("*", 3)
* printItToo ("*", 2)
* printItToo ("*", 1)
printItToo ("*", 0) base case Notice that a base case is
*
eventually reached.

49

49

Infinite Recursion
• if the base case(s) is missing, or never reached, a
recursive method will run forever (or until the computer
runs out of resources)

public static void printItForever(String s, int n) {


// missing base case; infinite recursion
System.out.print(s);
printItForever(s, n - 1);
}

printItForever("*", 1)
* printItForever("*", 0)
* printItForever("*", -1)
* printItForever("*", -2) ...........

50

50

25
2020-09-23

Example 4B sometimes order matters

Printing n of Something
• suppose you want to implement a method that prints out
n+(n-1)+(n-2) ..1 copies of a string
▪ n times first line, n-1 times second line, n-2 times 3rd line…

public static void printIt(String s, int n) {


printIt("Hi", 3)
HiHiHi
for(int i = n; i >=1; i--) { HiHi
// print n time Hi
for(int j=0; j<i;j++)
System.out.print(s); printIt("*", 4)
****
System.out.println(); ***
} **
} *
51

51

A Different (recursive) Solution


We can use the following algorithm:
▪ if n == 0 done
▪ otherwise:
1. print the string n times in a line
2. print the string (n – 1) more times -- recursion

public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
else {
for(int i=0;i<n;i++)
System.out.print(s);
System.out.println();
printItToo(s, n - 1); // method invokes itself
}
52 }

52

26
2020-09-23

Recursion public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
else {
a method that calls itself is called
for(int a recursive method
i=0;i<n;i++)
a recursive method solves System.out.print(s);
a problem by repeatedly
System.out.println();
reducing the problem so that a base case can be reached
printItToo(s, n - 1); // method invokes itself
}
}

printItToo("*", 5)
***** printItToo ("*", 4)
**** printItToo ("*", 3)
*** printItToo ("*", 2)
** printItToo ("*", 1)
* printItToo ("*", 0) base case

53

53

Example 4C order matters

What if change the order:


▪ if n == 0 done
▪ otherwise:
1. print the string (n – 1) more times -- recursion
2. print the string n times in a line

public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
else {
printItToo(s, n - 1); // method invokes itself

for(int i=0;i<n;i++)
System.out.print(s);
System.out.println();
54
}
54 }

27
2020-09-23

Recursion public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
else {
a method that calls itself is called na-recursive
printItToo(s, 1); method
// method invokes itself
a recursive method solves a problem by repeatedly
for(int i=0;i<n;i++)
reducing the problem so System.out.print(s);
that a base case can be reached
System.out.println();
}
}

printItToo("*", 5)
printItToo ("*", 4)
printItToo ("*", 3)
printItToo ("*", 2)
printItToo ("*", 1) *
printItToo ("*", 0) **
***
****
*****
What is the output? Same as 4b?
55 Anything printed, in the first 5 recursive function calls?

55

public static void printItToo("*", 4)


{
if (n == 0) { public static void printItToo("*", 3)
return; {
} if (n == 0) {
else { return;
printItToo("*", 4 - 1); // recursive call }
else {
for(int i=0;i<4;i++) printItToo("*", 3 - 1); // recursive call
System.out.print("*");
for(int i=0;i<3;i++)
8 System.out.print("*");
16

public static void printItToo("*", 2)


printItToo("*", 4) {
if (n == 0) {
* return;
** }
else {
*** printItToo("*", 2 - 1); // recursive call
****
for(int i=0;i<2;i++)
System.out.print("*");

public static void printItToo("*", 1)


public static void printItToo("*", 0) {
{ if (n == 0) {
if (n == 0) { return;
return; }
} else {
else { printItToo("*", 1 - 1); // recursive call
printItToo("*", 3 - 1); // recursive call
for(int i=0;i<1;i++)
56for(int i=0;i<n;i++) System.out.print("*");
System.out.print("*");

56

28
2020-09-23

Example 4b 4c order matters


public static void printItToo(String s, int n) { printItToo("*", 5)
if (n == 0) {
return;
}
else { *****
for(int i=0;i<n;i++) ****
System.out.print(s);
***
**
System.out.println(); *
printItToo(s, n - 1); // method invokes itself
}
}

public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
*
else { **
printItToo(s, n - 1); // method invokes itself ***
****
for(int i=0;i<n;i++) *****
System.out.print(s);
System.out.println();
}
}

57

57

What if reorder example 4a? Will change output?


public static void printItToo(String s, int n) { printItToo("*", 5)
if (n == 0) {
return;
}
else { *
System.out.println(s); *
*
*
printItToo(s, n - 1); // method invokes itself *
}
}

public static void printItToo(String s, int n) {


if (n == 0) {
return;
}
else {
printItToo(s, n - 1); // method invokes itself

System.out.println(s);
}
}

58

58

29
2020-09-23

Example 5 multiple subproblems

Fibonacci Numbers
• the sequence of additional pairs
▪ 0, 1, 1, 2, 3, 5, 8, 13, ...
are called Fibonacci numbers
▪ Each number in the series is sum of two previous numbers

• base cases
▪ F(0) = 0
▪ F(1) = 1
• recursive definition
▪ F(n) = F(n – 1) + F(n – 2)

59

59

Iterative Fibonacci 0, 1, 1, 2, 3, 5, 8, 13, ...

public static long fib(int n) {


if ( n < 2 )
return 1;
long answer = 0;
long prevFib=1, prev2Fib=0; // fib(1) & fib(0)

for (int k = 2; k <= n; k++) {


answer = prevFib + prev2Fib;
prev2Fib = prevFib;
prevFib = answer;
}
return answer;
}

60

60

30
2020-09-23

Recursive Fibonacci
• example: compute the n’th Fibonacci number

public static int fibonacci(int n) {


if (n == 0) {
return 0;
}
else if (n == 1) {
return 1;
}
else {
int f = fibonacci(n - 1) + fibonacci(n - 2);
return f;
}
}

61

61

static int fib(4) {


if (n == 0) return 0;
else if (n == 1) return 1;
return fib(4 - 1) + fib(4 - 2);

1 1
static int fib (3) { static int fib (2) {
if (n == 0) return 0; if (n == 0) return 0;
else if (n == 1) return 1; else if (n == 1) return 1;
return fib(3 - 1) + fib(3 - 2); return fib(2 - 1) + fib(2 - 2);

1 0 1 0
static int fib (2) { static int fib (1) { static int fib (1) { static int fib (0) {
if (n == 0) return 0; if (n == 0) return 0; if (n == 0) return 0; if (n == 0) return 0;
else if (n == 1) return 1; else if (n == 1) return 1; else if (n == 1) return 1; else if (n == 1) return
return fib (2-1) + fib (2-2); return fib (1-1) + … return fib (1-1) + … return fib (1-1) + …

1 0
static int fib (1) { static int fib (0) {
if (n == 0) return 0; if (n == 0) return 0; F(4)
else if (n == 1) return 1; else if (n == 1) return 1; F(3)
return fib (1-1) + … return fib (1-1) + …
F(2)
F(1)
F(0)
F(1)
F(2)
F(1)
Be careful about the call sequence F(0)
62 You should know that (be able trace)

62

31
2020-09-23

Fibonacci Call Tree F(5)

F(4)
public static int fibonacci(int n) {
F(3)
if (n == 0)
return 0; F(2)
else if (n == 1) { F(1)
return 1; F(0)
F(1)
int f = fibonacci(n - 1) + fibonacci(n - 2); F(5) F(2)
return f;
F(1)
}
F(0)
F(3)
F(4) F(3) F(2)
F(1)
F(0)

F(1)
F(3) F(2) F(2) F(1)
1

F(2) F(1) F(1) F(0) F(1) F(0)


1 1 0 1 0

F(1) F(0)
1 0

Be careful about the call sequence Any


63 You should know that (be able to trace) problems

63

Recursive Definitions
• Recursion
▪ Process of solving a problem by reducing it to smaller versions
of itself
▪ It can be either *
o Decrease and conquer
❖ Reduce a problem of size n into a sub-problem of size n-b
❖ factorial(), length(), power(), zeros in array/string/number

o Divide and conquer


❖ Reduce a problem of size n into two or more sub-problem of size
n/b
❖ Fibonacci, mergeSort

▪ Some problems can be solved both ways.

* Another definition is that all recursions are “divide and conquer”

64

32
2020-09-23

Example 6a. Adding the Numbers in an Array:


Decrease and Conquer Approach
• Think recursively:
• The sum of n elements in the array is equal to the value of
the first element plus the sum of the all the remaining n-1
elements in the array
• Base case: array of size 1

Algorithm LinearRecSum(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
LinearSum(A, 0, 5) = 59
4 6 23 14 12
if n = 1 then
return A[i ];
else
return A[i] + LinearRecSum(A, i+1, n-1);

65

Linear Recursive Trace


Example: LinearRecSum(A, 0, 8):
A = 2 7 6 21 4 33 3 6
2+80 = 82
7+73
0, 8
1, 7 6+67

2, 6 21+46

3, 5 4+42

4, 4 33+9
5, 3 3+6
6, 2 6
7, 1

66

33
2020-09-23

Example 6b Adding the Numbers in an Array:


Divide and Conquer Approach
• Think recursively:
• For any array with a size greater than one, divide the array
into two sub-arrays of size n/2
• The sum of n elements in the array is equal to the sum of
one sub-array plus the sum of the other sub-array
• Base case: array of size 1

Algorithm BinaryRecSum(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
BinaryRecSum(A, 0, 5) = 59
4 6 23 14 12
if n = 1 then
return A[i];
else
return BinaryRecSum(A, i, ceil(n/ 2)) +
BinaryRecSum(A, i + ceil(n/ 2), floor(n/ 2))

67

Binary Recursive Trace


Algorithm BinaryRecSum(A, i, n):
if n = 1 then
return A[i];
else
return BinaryRecSum(A, i, n/ 2) + BinaryRecSum(A, i + (n/ 2), n/ 2)

Example: BinaryRecSum(A, 0, 8): (0,8)

A = (0,4)

2 7 6 21 4 33 3 6 (0,2)

(0,1)

A,0,8 82 (1,1)

(2,2)
(2,1)
A,0,4 36 A,4, 4 46 (3,1)
(4,4)
(4,2)
0, 2 9 2, 2 27 4, 2 37 6, 2 9 (4,1)
(5,1)

0, 1 1, 1 2, 1 3, 1 4, 1 5, 1 6, 1 7, 1 (6,2)

(6,1)
2 + 7 + 6 + 21 + 4 + 33 + 3 + 6 (7,1)

You should be able to trace the call sequence

68

34
2020-09-23

What is the size OS stack required to execute LinearRecSum and


BinaryRecSum, given that the size of the array is n??

A = 2 7 6 21 4 33 3 6

size of call stack: n size of function call stack: lg n

69

Towers of Hanoi
Problem
• Invented by Edouard Lucas, in 1883
• Given a tower of n disks, initially stacked in increasing
size on one of three pegs, the objective is to transfer the
entire tower to one of the other pegs, moving only one
disk at a time and never a larger one onto a smaller.

• Play it at
http://www.web-games-online.com/towers-of-
hanoi/index.php

70

35
2020-09-23

Illustration for Solution

Original Configuration Move 1

Move 2 Move 3

71

Move 3

Move 4 Move 5

Move 6 Move 7 (done)

4: 15 7: 127
5: 31 8: 255 Probably not possible non-recursively
6: 63 9: 511
72

36
2020-09-23

Towers of Hanoi: Think Recursively

Move top n-1 to peg 3


Assume solution to smaller problem

Move top n-1 to peg 2


Assume solution to smaller problem

73

Towers of Hanoi: Recursive Algorithm

void moveDisks(int count, int peg1, int peg2)


{
if(count > 0)
{
moveDisks(count - 1, peg1, peg3);

move disk n from peg1 to peg2 ;

moveDisks(count - 1, peg3, peg2);


}
}

74

37
2020-09-23

Towers of Hanoi: Recursive Algorithm


public void towersOfHanoi(int n, String source, String aux, String dest)
{
// If only 1 disk, make the move and return.
if(n==1)
{ source dest aux
System.out.println(n + "from" + source+" --> "+dest);
return;
}
// Move top n-1 disks from S to A, using D as auxiliary.
towersOfHanoi(n-1, source, dest, aux);
source dest aux
//Move remaining disks from S to D
System.out.println(n +"from" + source+" --> "+dest);

// Move n-1 disks from A to D, using S as auxiliary source dest aux


towersOfHanoi(n-1, aux, source, dest);
}

towerOfHanoi(2,'Souce','Aux','Dest'); source aux


dest
1 from Source → Aux
2 from Source → Dest
1 from Aux → Dest

75

Cost of Hanoi Towers


• How many moves is necessary to solve Hanoi Towers
problem for N rings?
moves(1) = 1
moves(N) = moves(N-1) + moves(1) + moves(N-1)
i.e.
moves(N) = 2*moves(N-1) + 1

4: 15
5: 31
6: 63
M(n) = 2 M(n-1) + 1
7: 127
8: 255
M(n) = 2n – 1
9: 511
… Big O = O(2n)
64:264 -1

If n = 64, and it takes 1 second to move a disk, then it will takes


584942417400 (585 billion) years to move all the disks.
76

76

38
2020-09-23

Summary of recursion
• Problems solved by recursion, can be solved by iterative methods too.
▪ Recursive solution usually is cleaner, short. E.g., Tower of Hanoi
▪ More efficient? See complexity later.

• Key is to think recursively, then implementation accordingly

• Sometimes, it is not an easy job, if possible at all, to solve a problem


using one single recursive function.
▪ It is possible to have more one base case.
▪ It is possible to have more than one recursive case.
▪ Linear (decrease) vs binary (divide) recursion.
o Not always trivial to reduce (zero in numbers)
o Repeated computation.
▪ Can call recursion earlier or later.
o Sometimes order matters

• Don’t expect that you can solve a recursive algorithm straight away.
▪ Need quite some practice
77

77

Summary of recursion

• Practice: how to solve the problems recursively? decrease/divide/both?


▪ Find max/min in array
▪ Count elements in an array who satisfy some criterion
o Even/odd number, > 5, multiples of 5…
▪ Check if a string/array is a Palindrome
▪ Move max/min to front of an int array
▪ Print string reversely

▪ (harder) Find all sub-sum of an int array

78

78

39

You might also like