0% found this document useful (0 votes)
35 views

Recursion

This document discusses recursion and provides examples of recursive functions including: 1) Factorial, which is defined recursively as n! = n * (n-1)! for n > 0 and n! = 1 for n = 0. 2) Binary search, which searches a sorted array by recursively checking either the lower or upper half of the array. 3) Fibonacci numbers, where each number is the sum of the previous two, defined recursively as Fib(n) = Fib(n-1) + Fib(n-2).

Uploaded by

Sanskar Seth
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

Recursion

This document discusses recursion and provides examples of recursive functions including: 1) Factorial, which is defined recursively as n! = n * (n-1)! for n > 0 and n! = 1 for n = 0. 2) Binary search, which searches a sorted array by recursively checking either the lower or upper half of the array. 3) Fibonacci numbers, where each number is the sum of the previous two, defined recursively as Fib(n) = Fib(n-1) + Fib(n-2).

Uploaded by

Sanskar Seth
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

Data Structures

Lecture 3 - Recursion

What Is Recursion?
• Recursion - defining the solution of a
problem in terms of itself except for one or
more primitive cases.
Is Factorial Recursive?
• The factorial function is defined as:
n! = n · (n-1) · (n-2 ) …· 1
or
n
n ! = ∏i = 1
i

• The recursive definition is:


n! = n (n-1)! for n > 0
n! = 1 for n = 0

Factorial function
• We can write a factorial function:
float factorial (int n)
{
float prod;
int n;

x = n;
prod = 1;
while (x != 0)
prod *= x--;
return(prod);
}
Factorial Function (continued)
• This is iterative; it performs a calculation
until a certain condition is met.
• By recursion:
(1) 5 ! = 5 • 4! (5’) 1! = 1 • 0! = 1 • 1 = 1
(2) 4! = 4 • 3! (4’) 2! = 2 • 1! = 2 • 1 = 2
(3) 3! = 3 • 2! (3’) 3! = 3 • 2! = 3 • 2 = 6
(4) 2! = 2 • 1! (2’) 4! = 4 • 3! = 4 • 4 = 24
(5) 1! = 1 • 0! (1’) 5! = 5 • 4! = 5 • 24 = 120
(6) 0 ≅ 1

Other Examples of Recursion

• Multiplication - a•b
a • b = a • (b-1) + a if b > 1
a•1=a if b = 1
• Fibonacci Numbers - 0, 1, 1, 2, 3, 5, 8,
13, 21 , …
Fib(n) = Fib(n-1) + Fib(n-2) if n > 1
Fib(n) = n if n = 1, n = 0
This is doubly recursive = 2 recursive call in the
definition
Binary Search
• A binary search is a fairly quick way to search for
data in a presorted data, sorted by key.
• Algorithm
Initialize low and high
If low > high THEN binsrch = 0
ELSE BEGIN
mid = (low + high) / 2
IF x = a[mid] THEN binsrch = mid
ELSE IF x < a[mid]
THEN search from low to mid-1
ELSE search from mid+1 to high
END

Binary Search (continued)


• Given numbers stored in an array sorted in
ascending order, search for 25:
i x[i]
1 2
2 4 Each pass:
3 5
4 6 Pass # Low High Mid
5 10 1 1 10 5
6 15 2 6 10 8
7 17 3 9 10 9
8 20
9 25 Found it!
10 32
Tracing the Recursive Factorial
• Writing a recursive factorial function:
float fact(int n)
{
int x;
float y;
if (n == 0)
return(1);
x = n – 1;
y = fact(x);
return(n*y);
}

Tracing Recursive Factorial (continued)


Let’s trace cout << fact(4);

3
4 4 3 -
n x y n x y n x y
Initially fact(4) fact(4)
0
1 -
1 0
2 2 1 -
2 1 -
3 2 - 3 2 - 3 2 -
4 3 - 4 3 - -
4 3
n x y n x y n x y
fact(2) fact(1) fact(0)
0 - 1
- 1 0 1
1 0
2 2 1 -
1 - 2 1 2
3 2 - 3 2 - 3 2 -
4 3 - 4 3 - -
4 3
n x y n x y n x y
if (n==0) return(1) y = fact(0) y = fact(1)

3 2 6
4 3 - 4 3 24
n x y n x y n x y
y = fact(2) y = fact(3) y = fact(1)
Recursive Multiplication
• We can also write a recursive multiplication function:
int mult(int a, int b)
{
if (b == 1)
return(a);
return(mult(a, b-1) + a);
}

or
int mult(int a, int b)
{
return(b == 1? a : mult(a, b-1) + a);
}

Rewriting fact
• We can rewrite fact as
float fact(int n)
{
return(n == 0? 1 : n * fact(n-1));
}
• What about fact(-1)? We want to catch the error
and our current function does not.
Rewriting fact (continued)
float fact(int n)
{
int x;
if (n < 0){
cerr << “Negative parameter in”
<< “ factorial function\n”;
exit(1);
}
return ((n == 0)? 1 : n * fact(n-1));
}

Writing the Fibonacci Function

• Fn = Fn-1 + F n-2 for n > 1


• F0 = 0 ; F1 = 1
int fib(int n)
{
int x, y;
if (n >= 1)
return(n);
x = fib(n-1);
y = fib(n-2)
return(x + y);
}
Tracing Recursive Fibonacci (continued)
Let’s trace cout << fib(6);

4
5 5 * *
6 6 * * 6 * *
n x y n x y n x y

4
5 5 * *
6 6 * * 6 * *
n x y n x y n x y
1 1
2 2 * *
3 3 3
* * * *
4 * * 4 * * 4 * *
5 * * 5 * * 5 * *
6 * * 6 * * 6 * *
n x y n x y n x y
0 0
2 1 2 * * 2 1 0
*
3 3 * * 3 * *
* *
4 * * 4 * * 4 * *
5 * * 5 * * 5 * *
6 * * 6 * * 6 * *

n x y n x y n x y

1
3 1 * 3
3 1 * 1 1
4 * * 4 4 * *
* *
5 * * 5 * * 5 * *
6 * * 6 * * 6 * *
n x y n x y n x y

1
2 2 * *
4 2 * 4 2 * 4 2 *
5 * * 5 * * 5 * *
6 * * 6 * * 6 * *
n x y n x y n x y

0 0
2 1 * 2 1 * 2 1 0
4 2 * 4 2 * 4 2 *
5 * * 5 * * 5 * *
6 * * 6 * * 6 * *
n x y n x y n x y
4 2 1
5 * * 5 3 *
6 * * 6 * *
n x y n x y

Writing the Binary Search


• We invoke the binary search by writing:
i = binsrch(a, x);
It will check the array a for an integer x.
// binsrch() - The classic binary search algorithm
// written recursively. It requires
// that the array, search key and bounds
// of the subarray being searched be
// passed as parameters.
int binsrch(int a[], int x, int low, int high)
{
int mid;
if (low > high)
return(-1); // Not in the array
mid = (low + high)/2;
return((x == a[mid])? mid :
(x < a[mid])?
//Check the lower half
binsrch(a, x, low, mid-1):
//Check the upper half
binsrch(a, x, mid+1, high));
}

Revising the Binary Search


• Passing a and x should not be necessary
since they do change from one recursive
call to the next. Let’s make them global:
int a[ArraySize];
int x;
and it’s called by
i = binsrch(0, n-1);
Revised Binary Search
int binsrch(int low, int high)
{
int mid;

if (low > high)


return(-1); // Not in the array
mid = (low + high)/2;
return((x == a[mid])? mid :
(x < a[mid])?
//Check the lower half
binsrch(low, mid-1):
//Check the upper half
binsrch(mid+1, high));
}

Recursive Chains

• A recursive function does not have to call


itself directly; it can call another function
which in turn called the first function:
• a(…) b(…)
{ {
…. ….
b(…); a(…);
} }
• This is called a recursive chain.
Recursive Definition of Algebraic Expression

• An example of a recursive chain might be an


algebraic expression where:
– An expression is a term followed by a plus sign
followed by term or a single term.
– A term is a factor followed by a asterisk followed by
factor or a single factor.
– A factor is either a letter or an expression enclosed in
parentheses.

The expr Program


#include <iostream.h>
#include <string.h>
#include <ctype.h>

enum boolean {false, true};


const int MaxStringSize = 100;

int getsymb(char str[], int length, int &pos);


void readstr(char *instring, int &inlength);
int expr(char str[], int length, int &pos);
int term(char str[], int length, int &pos);
int factor(char str[], int length, int &pos);
// main() - This program allows a user to test whether
// an expression is valid or invalid. All
// variables and constants are restricted to
// one character.
int main(void)
{
char str[MaxStringSize];
int length, pos;

readstr(str, length);
pos = 0;

if (expr(str, length, pos) && pos >= length)


cout << "Valid expression" << endl;
else
cout << "Invalid expression" << endl;
// The condition can fail for one (or both) of two
// reason. If expr(str, length, pos) == false
// then there is no valid expression beginning at
// pos. If pos < length there may be a valid
// expression starting at pos but it does occupy
// the entire string.
return(0);
}
// expr() - Returns true if str is a valid expression
// Returns false is str is not.
int expr(char str[], int length, int &pos)
{
// Look for a term
if (term(str, length, pos) == false)
return(false);

// We have found a term – now look at the


// next symbol

if (getsymb(str, length, pos) != '+') {


// We have found the longest expression
// (a single term). Reposition pos so it
// refers to the last position of the
// expression
--pos;
return(true);
}
// At this point, we have found a term and a
// plus sign. We must looj for another term
return(term(str, length, pos));
}
// term() - Returns true if str is a valid term
// Returns false is str is not.
int term(char str[], int length, int &pos)
{
if (factor(str, length, pos) == false)
return(false);

if (getsymb(str, length, pos) != '*') {


--pos;
return(true);
}
return(factor(str, length, pos));
}

// factor() - Returns true if str is a valid factor


// Returns false is str is not.
int factor(char str[], int length, int &pos)
{
int c;

if ((c = getsymb(str, length, pos)) != '(')


//The factor is not inside parentheses
return(isalpha(c));

// Examine parenthetic terms


return(expr(str, length, pos)
&& getsymb(str, length, pos) == ')');
}
//getsymb() - Returns the next character in the string str
int getsymb(char str[], int length, int &pos)
{
char c;
if (pos < length)
c = str[pos];
else
// Beyond the end of the line of text
c = ' ';
pos++;
return(c);
}

//readstr() - Reads a line of text that is assumed to be


// an expression
void readstr(char *instring, int &inlength)
{
cin >> instring;
inlength = strlen(instring);
}
Towers of Hanoi
The Tower of Hanoi gives us an example of a problem that
can only be solved by recursion:
1. Three pegs with n disks - the smallest disk is on top and
the largest is on the bottom.
2. A disk cannot beplaced on top of a smaller disk
3. Only one disk can be moved at a time.

The solution is:


1. Assume n-1 disks are moved onto the auxiliary disk
2. Move bottom disk to destination peg.
3. Move n-1 disks to destination peg.
4. If n=1, move the only disk to the destination peg.

Example - Towers of Hanoi For 3 Disks

1.

2.
3.

4.

Towers of Hanoi Program

#include <iostream.h>

void towers (int n, char frompeg, char topeg, char


auxpeg);

// main() - A driver for the towers function


int main(void)
{
int n;

cout << "How many disks on the Towers of”


“ Hanoi ?";
cin >> n;
towers(n, 'A', 'C', 'B');
return(0);
}
// towers() - A recursive solution to the Tower of
// Hanoi Problem
void towers (int n, char frompeg, char topeg,
char auxpeg)
{
// If only one disk, make the move and return
if (n==1) {
cout << "Move disk 1 from peg ”
<< frompeg << " to peg ”
<< topeg << endl;
return;
}

// Move top n-1 disks from A to B using C as


// auxiliary
towers(n-1, frompeg, auxpeg, topeg);

// Move remaining disk from A to C


cout << "Move disk " << n << " from peg ”
<< frompeg << " to peg " << topeg
<< endl;

// Move n-1 disks from B to CB using A as


// auxiliary
towers(n-1, auxpeg, topeg, frompeg);

}
Simulating Recursion
• Being able to simulate recursion is important
because:
– Many programming languages do not
implement it, .e.g., FORTRAN, COBOL,
assembler, etc.
– It teaches us the implementation of recursions
and its pitfalls.
– Recursion is often more expensive
computationally than we find we can afford.
• In order to simulate recursion, we must understand
how function calls and function returns work.

Calling a function
Calling a function consists of 3 actions:
– Passing arguments (or parameters)
A copy of the parameter is made locally within the
function and any changes to the parameter are made to
the local copy.
– Allocating and initializing local variables
These local variables include those declared directly in
the functions and any temporary variables that must
be created during execution e.,g., if we add
x + y + z, we need a place to store x + y
temporarily.
– Transferring control to the function
Save the return address and transfer control to the
function
Returning From a Function Call

Returning from a function call consists of the


following steps:
– The return address is retrieved and saved in a
safe place (i.e., outside the function’s data
area).
– The function’s data area is freed.
– The function returns control by branching to
the return address.

Returning From a Function Call


main program procedure b procedure c procedure d
Return address Return address Return address

call proc. d
call proc. b call proc. c

Control

main program procedure b procedure c


Return address Return address

call proc. d
call proc. b call proc. c

Control
Implementing Recursive Functions

typedef struct {
int param;
int x;
long y;
short retaddr;
} dataarea;

class stack {
public:
boolean empty(void);
dataarea pop(void);
void push(dataarea x);
dataarea stacktop(void);
boolean popandtest(dataarea &x); //Tests
before popping
boolean pushandtest(dataarea x); stack(void);
//Default constructor
stack(dataarea x); //Init. constructor
private:
int top;
dataarea item[StackSize];
};
Simfact function
int simfact(int n)
{
dataarea currarea;
stack s;
short i;
long result;

// Initialize a dummy data area


currarea.param = 0;
currarea.x = 0;
currarea.y = 0;
currarea.retaddr = 0;

// Push the data data area onto the stack


s.push(currarea);

// Set the parametetr and the return address


// of the current data area to their proper
// values
currarea.param = n;
currarea.retaddr = 1;
//This is the beginning of the simulated
// factorial routine
start:
if (currarea.param == 0) {
// Simulation of return(1)
result = 1;
i = currarea.retaddr;
currarea = s.pop();
switch(i) {
case 1: goto label1;
case 2: goto label2;
}
}

currarea.x = currarea.param - 1;
//Simulation of recursive call to fact
s.push(currarea);
currarea.param = currarea.x;
currarea.retaddr = 2;
goto start;

// This is the point to which we return from


// the recursive call.
// Set currarea.y to the returned value
label2:
currarea.y = result;
// Simulation of return(n*y);
result = currarea.param * currarea.y;
i = currarea.retaddr;
currarea = s.pop();
switch(i) {
case 1: goto label1;
case 2: goto label2;
}

//At this point we return to the main routine


label1:
return(result);
}
Improving simfact

• Do we need to stack all the local variables


in this routine?
– n changes and is used again after returning from
a recursive call.
– x is never used again after the recursive call
– y is not used until after we return.
– We can avoid saving the return address if we
use stack underflow as a criterion for exiting
the routine.

simfact with a limited stack


int simfact(int n)
{
stack s; //s stacks only the current
// parameter
short und;
long result, y;
int currparam, x;

// Set the parameter and the return address


// of the current data area to their proper
// values
currparam = n;
//This is the beginning of the simulated
// factorial routine
start:
if (currparam == 0) {
// Simulation of return(1)
result = 1;
und = s.popandtest(currparam);
switch(und) {
case true: goto label1;
case false: goto label2;
}
}

// currparam != 0
x = currparam - 1;
//Simulation of recursive call to fact
s.push(currparam);
currparam = x;
goto start;

// This is the point to which we return from


// the recursive call.
// Set y to the returned value
label2:
y = result;
// Simulation of return(n*y);
result = currparam * y;
und = s.popandtest(currparam);
switch(und) {
case true: goto label1;
case false: goto label2;
}

//At this point we return to the main


routine
label1:
return(result);
}
Eliminating gotos
• Goto is a bad programming form because it
obscures the meaning and intent of the
algorithm.
• We will combine the two references to
popandtest and switch(und) into
one.

simfact with one switch


int simfact(int n)
{
stack s;
short und;
long y;
int x;

x = n;
//This is the beginning of the simulated
// factorial routine
start:
if (x == 0)
y = 1;
else {
s.push(x--);
goto start;
}
label1:
und=s.popandtest(x);
if (und == true)
return(y);

label2:
y *= x;
goto label1;

Eliminating the gotos

• We recognize that there are really two


loops:
– one loop which generates additional function
call (simulated by pushing the parameter on the
stack)
– another loop where we return from recursive
calls (simulated by popping the parameter off
the stack).
simfact without gotos
int simfact(int n)
{
stack s;
short und;
long y;
int x;

x = n;

//This is the beginning of the simulated


// factorial routine
start:
while (x != 0)
s.push(x--);
y = 1;
und=s.popandtest(x);

label1:
while (und == false) {
y *= x;
und = s.popandtest(x);
}

return(y);
}
Finally…after eliminating the
unnecessary pushes and pops...

int simfact(int n)
{
long y;
int x;

for (y = x= 1; x <= n; x++)


y *= x;

return(y);
}

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy