Top Banner
1 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]
24

224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

Jan 01, 2016

Download

Documents

Eugene Collins
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

13/30/98

CSE 143

Recursion[Sections 6.1,6.3-6.7]

Page 2: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

23/30/98

Recursion

A recursive definition is one which is defined in terms of itselfExample:

Compound interest: “The value after 10 years is equal to the interest rate times the value after 9 years.”

A phrase is a "palindrome" if the 1st and last letters are the same, and what's inside is itself a palindrome (or is empty).

Page 3: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

33/30/98

Computer Science ExamplesRecursive procedure: a procedure that invokes itself

Recursive data structures: a data structure may contain a pointer to an instance of the same type

struct Node {

int data;

Node *next;

};

Recursive definitions: if A and B are postfix expressions, then A B + is a postfix expression

Page 4: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

43/30/98

Factorialn! ( “n factorial” ) can be defined in two ways:

Non-recursive definition

n! = n * (n-1) * (n-2) … * 2 * 1Recursive definition

n! =

1 , if n = 1

n (n-1)! , if n > 1

PS: 0! is usually defined to be 1

Page 5: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

53/30/98

Factorial (2) How do we write a function that reflects the

recursive definition?int factorial(int n) {

if ( n == 1 )

return 1;

else

return n * factorial(n-1);

}

Note that the factorial function invokes itself.

How can this work?

Page 6: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

63/30/98

Activation RecordsRemember that local variables and formal params are allocated when { } block is entered, deleted when block is exited.

Here's how: Whenever a function is called (or { } block is entered), a new activation record is pushed on the runtime stack, containing:

-- a separate copy of all local variables and parameters-- control info (e.g. return address)

Activation record is popped at end of function (or block)A recursive function call is no different in this respectEach recursive call has its own copy of locals

Page 7: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

73/30/98

Exampleint factorial(int n) {

if ( n == 1 )

return 1;

else

return n * factorial(n-1);

}

…int main (void) {

int x = factorial(4);

cout << “4! = “ << x << endl;

...

factorial(4)4*factorial(3)=4 * 6 = 24

factorial(3)3*factorial(2)=3 * 2 = 6

factorial(2)2*factorial(1)=2 * 1 = 2

factorial(1)return 1

1

2

3

4

main()X=factorial(4)

Page 8: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

83/30/98

Infinite Recursion Must always have some way to make recursion

stop, otherwise it runs forever:int BadFactorial(n) {

int x = BadFactorial(n-1);

if ( n == 1 )

return 1;

else

return n * x;

}

What is the value of BadFactorial(2)?

Page 9: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

93/30/98

Infinite Recursion (2)Similar problems with incorrect recursive definitions of data types:

struct Node {

int data;

Node next;

};

How does Node *next; solve this problem?

data

next

...

Page 10: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

103/30/98

Using Recursion ProperlyFor correct recursion (recursion that does something useful and eventually stops), need two parts:1. One or more base cases that are not recursive

if ( n == 1 ) return 1;

2. One or more recursive cases that operate on smaller problems that get closer to the base case(s)

return n * factorial(n-1);

The base case(s) should always be checked before the recursive calls

Page 11: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

113/30/98

Recursive Data StructuresMany data structures are defined in terms of (pointers to) other instances of themselvesLinked Lists are one example:struct Node {

int data;

Node *next;

};

Trees (coming soon) are another example

Recursive data structures suggest recursive algorithms

Page 12: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

123/30/98

Printing a Linked List

void print(Node* first) {if (first == NULL)

return; else {

cout << first->data << “ “; print(first->next);

}}

How many recursive calls do we make when printing the list <1,2,3,4>?

Page 13: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

133/30/98

Printing in Reverse Order•At first, seems difficult

•All the pointers point only forward.

•Recursion to the rescue!

void print(Node* first) {if (first == NULL)

return; else {

print(first->next); cout << first->data << “ “; }

}

Page 14: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

143/30/98

Summing a List

int listSum(Node* list) { if (list == NULL) return 0; // empty list has sum == 0 else return list->data + listSum(list->next);}

How would you modify this to count the length of a list? Add N to each element of a list?

Page 15: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

153/30/98

Recursion: Not Just for Lists

double sum (double iArray [ ], int from, int to) {//find the sum of all elements in the array between "from" and "to"

if (from > to)

return 0.0;

return iArray[from] + sum (iArray, from+1, to);

}...

double CashValues[200];

...

double total = sum (CashValues, 0, 199);

Page 16: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

163/30/98

Recursion w/helper functiondouble distance (double vec1[ ], double vec2[ ], int dim) { return sqrt (sumSqDiffs (vec1, vec2, 0, dim);}

double sumSqDiffs (double vec1[ ], double vec2[ ], int curr, int length) {

if (curr >= length) return 0.0; double remainingSqDiff = sumSqDiffs (vec1, vec2, curr+1, length); double currDiff = vec1[curr] - vec2[curr]; return (currDiff*currDiff) + remainingSqDiff;}

Page 17: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

173/30/98

Insist without Iteratingchar InsistOnYorN (void) {

char answer;

cout << endl << "Please enter y or n: ";

cin >> answer;

switch (answer) {

case 'y': return 'y';

case 'n': return 'n';

default:

return InsistOnYorN( );

}

}

Page 18: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

183/30/98

What does this function do?

int mystery (int x) { assert (x > 0); if (x == 1) return 0; int temp = mystery (x / 2); return 1 + temp;}

Page 19: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

193/30/98

Notes on Scope in C++The scope of an identifier is the part of the program in which the id is known

For local variables and formal params, scope starts at the declaration and continues until the closing } of its block.

int tree (double trunk) { //start trunk scope

int leaf; //start leaf scope

{ char acorn='a'; //start acorn scope

leaf = (int) acorn;

} //end acorn scope

return (int) trunk+leaf;

} //end leaf and trunk scope

Page 20: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

203/30/98

Scope in for loopsIn C: loop variable must be declared above loop, and remains valid after the loop:int i;

for (i = 0; i < 10; i++) cout << " " << i;

cout << endl << i; // perfectly legal

In pre-standard C++, loop var. may be declared in control, and still valid afterward:for (int i; i < 10; i++) cout << " " << i;

cout << endl << i; // perfectly legal

In new standard, var. is invalid after the loop:for (int i; i < 10; i++) cout << " " << i;

cout << endl << i; // syntax error here

PS: The original C style is still usable in C++

Page 21: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

213/30/98

Avoid Scope "Holes"Reusing an identifier "hides" an existing definition of the same identifier

Suppose class MyC has a member named a.void MyC::scopetest (int a) {//param a hides member a

int a;//syntax error -- can't hide param

{ int a; //not an error -- hides param a

} //param unhidden

}

The rules are complex. Best to avoid confusion: DON'T reuse identifiers in the same scope!

DON'T DON'T DON'T !!

Page 22: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

223/30/98

Recursion vs. IterationWhen to use recursion?

Processing recursive data structures“Divide & Conquer” algorithms:

1. Divide problem into subproblems2. Solve each subproblem recursively3. Combine subproblem solutions

When to use iteration instead?Nonrecursive data structuresProblems without obvious recursive structure

Any iteration can be rewritten using recursion, and vice-versa (at least in theory)

Iteration is generally faster

Page 23: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

233/30/98

Which is Better?If a single recursive call is at the very end of the function:Known as tail recursionEasy to rewrite iteratively (many good compilers can actually do this for you…)

Recursive problems that are not tail recursive are harder to write nonrecursivelyUsually have to simulate recursion with a stack

Some programming languages provide no loops (loops are implemented through if and recursion)

Page 24: 224 3/30/98 CSE 143 Recursion [Sections 6.1,6.3-6.7]

243/30/98

SummaryRecursion is something defined in terms of itself

Recursive proceduresRecursive data structuresRecursive definitions

Activation records make it workTwo parts of all recursive functions

Base case(s)Recursive case(s)Base case always checked first

ExamplesFactorial (n!)Recursive linked list manipulationUser input checking