Welcome to Scribd. Sign in or start your free trial to enjoy unlimited e-books, audiobooks & documents.Find out more
Standard view
Full view
of .
0 of .
Results for:
P. 1
Handout 4 - Recursion

# Handout 4 - Recursion

Ratings: (0)|Views: 307|Likes:

### Availability:

See more
See less

03/18/2014

pdf

text

original

Mekelle University Faculty of Business & Economics
Computer Science Department
ICT241: Data Structures and Algorithms
Handout 4 \u2013 Recursion

Handout Overview

This handout describes what is meant by a recursive definition to a problem, and how this translates into recursive C++ code. In particular, the way in which recursion is handled using the run-time stack is explained. A number of different types of recursion are highlighted: tail recursion, nontail recursion, indirect recursion and nested recursion. The dangers of excessive recursion are pointed out. Finally, the algorithmic concept of backtracking is described.

1. Recursive Definitions

Many programming languages, including C++, support the use ofrecursion to solve problems. Recursion is the ability of a function to call itself. For many problems, a solution that is defined in terms of itself seems a natural and intuitive one. For example, the factorial function can be defined as follows:

1
if n=0 (anchor)
n! =
n\u2219(n \u2013 1)! if n>0 (inductive step)
In mathematical terms, we can say that this recursive definition consists of two
parts: theanchor and the inductive step. The anchor is also sometimes called the
ground case, and is the case where we know how to compute the answer without

recursion. The inductive step is where we can only specify the answer by referring to the function itself. In the factorial case, the inductive step states that the factorial ofn is equal ton multiplied by the factorial ofn-1. So if we want to compute the factorial of 3 using this definition, we first use the inductive step,3!

= 3 * 2!. Next we need to find the result of the 2! term: again using the inductive
step we find that 2! = 2 * 1!. To compute the1! term, we use the inductive step
again: 1! = 1 * 0!. Finally to calculate0! we use the anchor, and get the result that
0! = 1. Therefore, the overall result of the calculation is that 3! = 3 * (2 * (1 * 1))
=6.
As another example, consider the following recursive definition:
1
if n=0 (anchor)
g(n) =
2\u2219g(n - 1) if n>0 (inductive step)
1
This recursive definition can be converted into the following simple non-recursive
formula:
g(n) = 2n

In fact it is often the case that a recursive definition can be replaced with a non- recursive one. Later in this handout we will look at how to choose the best definition (i.e. recursive or non-recursive) for a particular situation.

Converting simple recursive definitions like those given above into C++ code is
often a trivial task. For example, the C++ equivalent of the factorial definition is:
int factorial (int n) {
if (n == 0)
return 1;
else return n * factorial (n \u2013 1);
}

This code may appear strange at first. How can a C++ function call itself? To answer this question, it will be useful to examine exactly what happens when a function is called.

2. Function Calls and Recursive Implementation

When a function is called, the operating system needs to store some information about the function. First, the values of any arguments to the function need to be remembered. Also, if the function has any local variables, then their values must also be stored. If the function returns a value, then memory must be allocated to hold this return value. Finally, after the function has finished executing, the system needs to know something about the function that originally called this function, so that execution can resume where it left off before the function call.

As an example, consider the code in Figure 1. This contains a simple program with two functions,f1 andf2, in addition to themain function. To begin with, themain function callsf1 with a single argument. Next,f1 callsf2 with two arguments. Therefore, during program execution, there are three function calls in total (the system calls themain function to start the program). Whenever a function call is made we need to store information about the called function, as described above. However, we also need to remember the information about the calling function, and when the called function finishes execution we need to recall this information. This remembering and recalling of information needs to be done on alast-in/first-out basis, e.g. whenf2 finishes execution we need to recall the information forf1, and whenf1 finishes execution we need to recall the information for themain function. In other words, when a function finishes execution we need to remember the most recently stored function information: that of its calling function. We have already seen in Handout 3 that a stack provideslast-in/ first-out data access, so this would seem the natural choice of data structure for this situation.

2
//************** Figure 1.cpp*****************

// simple program to illustrate function calling
#include <iostream.h>
int f1 (int);

int f2 (int, int);

main () {
int x;
cout << \u201cEnter n:\n\u201d;
cin >> x;
cout << \u201cResult = \u201c << f1(x) << endl;

}int f1 (int n) {

int x;
x = f2 (n \u2013 1, n \u2013 2);
return x * 2;

}int f2 (int a, int b) {

int x;
x = a * b / 2;
return x;

}
Figure 1 \u2013 A simple program to illustrate function calling
The run-time stack is a data structure that is used by the operating system to store
activation records, or stack frames. Activation records typically contain the
following information:

\u2022Values for arguments to the function.
\u2022The return address to resume control by the caller.
\u2022A dynamic link, which is a pointer to the caller\u2019s activation record.
\u2022Values for all local variables in the function.
\u2022The return value of the function, if it has one.

Each time a function is called, the system creates an activation record that contains space to record all this information, assigns values for the first three, and pushes the record onto the run-time stack. When a function finishes execution, the dynamic link is used to restore the caller\u2019s activation record, and the return address is used to resume execution from the correct place. At all times there is a

stack pointer that points to the current activation record. Figure 2 illustrates the

contents of the run-time stack during execution off2() in the above example. Whenf2() finishes execution its activation record will be popped by moving the stack pointer down to the activation record forf1(), as indicated by the dynamic link in the activation record forf2().

3