23
M180: Data Structures & Algorithms in Java Recursion Arab Open University 1

M180: Data Structures & Algorithms in Java Recursion Arab Open University 1

Embed Size (px)

Citation preview

1

M180: Data Structures & Algorithms in Java

Recursion

Arab Open University

2

Recursive thinking• Recursion: a programming technique in which a

method can call itself to solve a problem

• recursive definition: one which uses the concept being defined in the definition itself– In some situations, a recursive definition can be an

appropriate or elegant way to express a concept.

• Before applying recursion to programming, it is best to practice thinking recursively

3

A recursive algorithm

• Consider the task of finding out what place you are in a long line of people.– If you cannot see the front of the line, you could ask the

person in front of you.– To answer your question, this person could ask the person in

front of him/her, and so on.

4

A recursive algorithm

• Once the front person answers their place in line (first), this information is handed back, one person at a time, until it reaches you.– This is the essence of recursive algorithms; many

invocations of the same method each solve a small part of a large problem.

Infinite Recursion

• All recursive definitions have to have a non-recursive part

• If they didn't, there would be no way to terminate the recursive path

• Such a definition would cause infinite recursion

• This problem is similar to an infinite loop, but the non-terminating "loop" is part of the definition itself

• The non-recursive part is often called the base case

Recursion

– Java's looping constructs make implementing this process easy.

sum(1) = 1sum(N) = N + sum(N - 1) if N > 1

– Consider what happens when the definition is applied to the problem of calculating sum(4):

sum(4) = 4 + sum(3) = 4 + 3 + sum(2)

= 4 + 3 + 2 + sum(1) = 4 + 3 + 2 + 1

– The fact that sum(1) is defined to be 1 without making reference to further invocations of sum saves the process from going on forever and the definition from being circular.

7

Factorial example• The factorial for any positive integer N, written N!, is

defined to be the product of all integers between 1 and N inclusive

// not recursivepublic static long factorial(int n) { long product = 1; for (int i = 1; i <= n; i++) { product *= i; } return product;}

n n n n! ( ) ( ) ... 1 2 1

8

Recursive factorial• factorial can also be defined recursively:

• A factorial is defined in terms of another factorial until the basic case of 0! is reached

// recursivepublic static long factorial(int n) { if (n == 0) { return 1; } else { return n * factorial(n - 1); }}

f nn n f n

n( )

( )

1 1

0 1

Recursive factorial

5!

5 * 4!

4 * 3!

3 * 2!

2 * 1!

1

2

6

24

120

10

Recursion vs. iteration

• every recursive solution has a corresponding iterative solution– For example, N ! can be calculated with a loop

• For some problems, recursive solutions are often more simple and elegant than iterative solutions

• You must be able to determine when recursion is appropriate

Recursive Programming

• Consider the problem of computing the sum of all the numbers between 1 and any positive integer N

• This problem can be recursively defined as:

3

1

2

1

1

11

21

1

N

i

N

i

N

i

N

i

iNNN

iNNiNi

Recursive Programming

// This method returns the sum of 1 to numpublic int sum (int num){ int result;

if (num == 1) result = 1; else result = num + sum (n-1);

return result;}

Recursive Programming

main

sum

sum

sum

sum(3)

sum(1)

sum(2)

result = 1

result = 3

result = 6

Fibonacci Recursion– Consider the definition of Fibonacci numbers below. – The first and second numbers in the Fibonacci sequence are 1. – Thereafter, each number is the sum of its two immediate

predecessors, as follows:

1 1 2 3 5 8 13 21 34 55 89 144 233 ... Or in other words:

fibonacci(1) = 1

fibonacci(2) = 1

fibonacci(N) = fibonacci(N - 1) + fibonacci(N - 2) if N > 2

– This is a recursive definition, and it is hard to imagine how one could express it no recursively.

Fibonacci Recursion

– As a second example of recursion, below is a method that calculates Fibonacci numbers:

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

Malformed Recursive Method

– Here is a subtler example of a malformed recursive method:

– This method works fine if n is odd, but when n is even, the method passes through the stopping state and keeps on going.

int badMethod (int n){ if (n == 1) return 1; else return n * badMethod(n - 2);}

Guidelines for Writing Recursive Methods

– A recursive method must have a well-defined termination or stopping state.

– For the factorial method, this was expressed in the lines:

– The recursive step, in which the method calls itself, must eventually lead to the stopping state.

– For the factorial method, the recursive step was expressed in the lines:

if (n == 1) return 1;

else return n * factorial(n - 1);

Guidelines for Writing Recursive Methods

– Because each invocation of the factorial method is passed a smaller value, eventually the stopping state must be reached.

– Had we accidentally written:

– the method would describe an infinite recursion. – Eventually, the user would notice and terminate the program,

or else the Java interpreter would run out memory, and the program would terminate with a stack overflow error.

else return n * factorial(n + 1);

• Example: 2030 has 2 zeros• If n has two or more digits

– the number of zeros is the number of zeros in n with the last digit removed

– plus an additional 1 if the last digit is zero• Examples:

– number of zeros in 20030 is number of zeros in 2003 plus 1

– number of zeros in 20031 is number of zeros in 2003 plus 0

Number of Zeros in a Number

recursive

numberOfZeros Recursive Design• numberOfZeros in the number N• K = number of digits in N• Decomposition:

– numberOfZeros in the first K - 1 digits– Last digit

• Composition:– Add:

• numberOfZeros in the first K – 1 digits• 1 if the last digit is zero

• Base case:– N has one digit (K = 1)

numberOfZeros Method

public static int numberOfZeros(int n){ int zeroCount;

if (n==0)zeroCount = 1;

else if (n < 10)zeroCount = 0;

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

else // n%10 != 0zeroCount = numberOfZeros(n/10);

return zeroCount;}

Which is (are) the base case(s)? Why?

Decompostion, Why?

Composition, why?

numberOfZeros(2005)

numberOfZeros(200) 5

numberOfZeros(20) 0

numberOfZeros(2) 0

Execution Trace (decomposition)

Each method invocation will execute one of the if-else cases shown at right.

public static int numberOfZeros(int n){ int zeroCount; if (n==0)

zeroCount = 1; else if (n < 10)

zeroCount = 0; else if (n%10 == 0)

zeroCount = numberOfZeros(n/10) + 1; else // n%10 != 0

zeroCount = numberOfZeros(n/10); return zeroCount;}

numberOfZeros(2005)->2

numberOfZeros(200)->2 5->0

numberOfZeros(20)->1 0->1

numberOfZeros(2)->0 0->1

Execution Trace (composition)

Recursive calls return

public static int numberOfZeros(int n){ int zeroCount; if (n==0)

zeroCount = 1; else if (n < 10)

zeroCount = 0; else if (n%10 == 0)

zeroCount = numberOfZeros(n/10) + 1; else // n%10 != 0

zeroCount = numberOfZeros(n/10); return zeroCount;}

+

+

+