Sie sind auf Seite 1von 19

Recursion

What is Recursion?
Recursive data structures
Recursive data algorithms

Programming and Data Structures

What is Recursion
The programs we have been doing to date have been organised into functions
that call another function in a disciplined, hierarchical way.
A function is said to be recursive if it calls itself either directly or indirectly
through another function.
A recursive function knows when to stop calling itself once a base case is
reached.
e.g. //print numbers 1 to n backwards
int print(int n)
{
if ( n = = 0)
// this is the terminating base case
return 0;
else {
System.out.print(+n+ );
return print(n-1); // recursive call to itself again
}
}

Programming and Data Structures

The Recursion Step


A recursive method tackles a problem by launching (calling) a copy of itself to work
on a smaller problem.
This is called the recursion step
The recursion step can result in many more such recursive calls as the method keeps
dividing each problem it is called with into two smaller problems.
It is important to ensure that the recursion terminates.
Each time the function calls itself with a slightly simpler version of the original problem.
This sequence of smaller problems must eventually converge on the base case.
Programming and Data Structures

Using Recursion Properly


To use recursion properly we need to remember to provide two parts:
1. One (or more) base cases that are not recursive, i.e. we can
directly give a solution:
if (n==0)
return 0;
2. One (or more) recursive cases that operate on smaller
problems that get closer to the base case(s)
return print(n-1);
NOTE: - The base case(s) should always be checked before the recursive calls.
- Most of the time, a recursive function will usually return something.
Programming and Data Structures

Infinite Recursion
Be very Careful! We must make sure that recursion eventually
stops, otherwise it will run forever (well not forever until we
run out of memory)
e.g.
// example of a badly defined recursive function
int Bad_recursion(n)
{
int x = Bad_recursion(n-1); // Bad!!!
if (n == 1)
return 1;
else
return n*x;
}

Programming and Data Structures

Recursion & Memory


Each recursive call makes a new copy of that method (actually only the variables) in
memory
Once a method ends (i.e. returns some data), the copy of that returning method is
removed from memory
In our previous example, if we called the print function with n=4, visually our memory
assignments may look like such :
Original calling method
print(4)
print(3)
returns 0

print(2)
returns 0

Returns 0 to main calling program

print(1)
returns 0

print(0)
returns 0

Programming and Data Structures

Recursive Algorithms
Recursive Algorithm : A solution that uses recursion to solve the problem
As mentioned previously, methods are said to be recursive if they call
themselves either directly or indirectly.
Many problems can be solved and best described through recursive
algorithms (e.g. traversal of nodes in a binary tree)
Some problems are best suited for recursive solutions while others are not.
Recursion is a complex topic and recursive algorithms can get quite complex.
We are going to look at some simple problems that can be solved recursively.

Programming and Data Structures

Solving Factorials
Factorial Explanation :
- The product of the positive integers from 1 to n inclusive is called
n factorial, usually denoted by n!
n! = n*(n-1)*(n-2)3*2*1

Mathematical explanation :
n! =

1,
n * (n-1)!

if n = 0
if n > 0

Example :
6! = 6*5*4*3*2*1 = 720
Programming and Data Structures

Solving Factorials Using iteration


We can write an iterative solution to the factorial problem :

Pseudo-Code

Actual Code

Begin
factorial(n)
result = 1; //init result i.e. n = 0 or 1
for i = 2 to n
// if n is > 1
result = result * i; //fact is n*(n-1)!
endfor
return result
endmethod
End

int factorial (int n)


{
int result = 1;
//init result i.e. n = 0 or 1
for(int i = 2; i<= n; i++)
// if n is > 1
{
result = result * i;
//fact is n*(n-1)!
}
return result
}

Programming and Data Structures

Solving Factorials Using Recursion


We can obtain a recursive definition of the factorial method by observing the relationship from
the previous mathematical explanation :
n! =

1,
n * (n-1)!

if n = 0
if n > 0

From this we can obtain both our base and recursive cases:
- Base Case :
if(n <= 1)
return 1;
- Recursive Case:
return n*factorial(n-1);
Now that we know both our base and recursive cases we can write our factorial method:
int factorial(int n)
{
if (n <=1) // the base case
return 1
else
return n * factorial(n-1);
}

// the recursive case

Programming and Data Structures

10

Visual Example - Factorials


Values returned from each recursive call

Recursive calls for 4!

Final result = 24

4!

4!

4*6  24 returned

4 * 3!

3 * 2!

4 * 3!
3*2 6 returned

3 * 2!
2 * 1  2 returned

2 * 1!

2 * 1!
1 returned

1
Programming and Data Structures

11

Recursion Vs. Iteration


So which way is better? iteration or recursion?
Answer is that it depends on what you are trying to do.
Usually, a recursive approach more naturally mirrors the problem at hand. So,
a recursive approach makes it simpler to tackle a problem which may not have
the most obvious of answers.
However, recursion carries an overhead that for each recursive call needs space
on the stack frame.
This extra memory need can be quite processor intensive and consume a lot of
memory if recursion is nested deeply
Iteration does not have this overhead as it occurs within the method so the
overhead of repeated method calls and extra memory is ommitted.
Programming and Data Structures

12

Recursion Vs. Iteration - Differences


Recursion

Iteration

terminates when a base case is reached.


Each recursive call requires extra space
on the stack frame (i.e. memory).

Terminates when a condition is proven to be


false.
Each iteration does not require any extra space
as it resides in the same method.

If we get infinite recursion, we will


eventually run out of memory, resulting in
a stack overflow.

an infinite loop could potentially loop forever


since there is no extra memory being created.

solutions to some problems are easier to


formulate recursively.

Iterative solutions to a problem may not


always be as obvious as a recursive solution.

Programming and Data Structures

13

Another Example - The Fibonacci Series


The Fibonacci series :
0, 1, 1, 2, 3, 5, 8, 13, 21, 34
begins with a 0 and 1 and has the property that each subsequent Fibonacci number is
the sum of the previous two Fibonnacci numbers
A Fibonnacci number, fib(n), can be expressed mathematically as :

fib(n) =

0
1,
fib(n-1) + fib(n-2),

if n = 0
if n = 1
if n > 1

Programming and Data Structures

14

The Fibonacci Function


Once again, we can obtain our base and recursive cases by observing the mathematical relationship
as we did before with factorials :
- Base case(s) :
if (n == 0) or (n == 1)
return n;
- Recursive case(s) :
return fib(n-1) + fib(n-2);

Now we may write the function to obtain a fibonnacci number :


unsigned int fib(unsigned int n)
{
if ((n == 0) || (n == 1)) // the base case
return n;
else
return fib(n-1) + fib(n-2); the recursive case
}

Note: In order to produce a fibonacci series we would iteratively call this function to produce the
required Fibonacci number in the series
Programming and Data Structures

15

Visual Example Fibonacci Series


Example for n = 3

fib(3)

Result of 2 is returned to the main calling program

returns fib(2) + fib(1)

returns fib(1) + fib(0)

returns 1

returns 1

returns 0

Programming and Data Structures

16

Fibonacci Series - Caution

A word of caution is in order about recursive programs like the one we use
here to generate Fibonacci numbers.

Each level of recursion in the fibonacci function has a doubling effect on the
number of calls.

Calculating the 20th Fibonacci number would require on the order of about a
million calls.

Calculating the 30th Fibonacci number would require around a billion calls.

This is referred to as exponential complexity.

How would you write it iteratively? Not as intuitive! Use a bottom up


dynamic programming solution.
Programming and Data Structures

17

Fibonacci Series Another Solution

One possibility to improve our recursive approach is to use arrays in a top-down


approach to remember the intermediate values calculated :
KnownFib[MAXSIZE] = unknown;
Fib(x)
if knownFib[x] <> unknown
return knownF[x]

// initialise all slots to indicate unknown

//if fib number x is known


// return known result

if x < 1
t = 0;
if x = 1
t = 1;
if x > 1
t = fib(x-1) + fib(x-2)

// if fib number x is 0
// then return 0
// if fib number x is 1
// return 1
// if fib number x is greater than 1
// recursively calculate this number

knownF[x] = t
return knownF[x]

// fib number x now known so put into our known array


// return fib number x

Note the index of our knownFib array represents each fib number x so that x can be used here as
an index into our knownFib array.
Programming and Data Structures

18

Classic Recursive Problems


Here are some classic problems that use recursion to solve:
- Queens Problem
- Knights Problem
- Towers of Hanoi

Some more complex sorting algorithms require that we use a


recursive approach in order to solve them. Two of these that we
will be looking at later on are :
- MergeSort
- QuickSort

Programming and Data Structures

19

Das könnte Ihnen auch gefallen