0% found this document useful (0 votes)
73 views39 pages

Post - 2030 L4

The document discusses recursion in programming. Recursion is defined as defining a problem or its solution in terms of itself. It involves base cases, where the solution is directly obtained, and recursive calls to smaller instances of the problem. Examples given include calculating length of a string and factorials. Recursion allows some problems to be solved more elegantly through reduction to base cases.

Uploaded by

Ya Lan
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)
73 views39 pages

Post - 2030 L4

The document discusses recursion in programming. Recursion is defined as defining a problem or its solution in terms of itself. It involves base cases, where the solution is directly obtained, and recursive calls to smaller instances of the problem. Examples given include calculating length of a string and factorials. Recursion allows some problems to be solved more elegantly through reduction to base cases.

Uploaded by

Ya Lan
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/ 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