1 / 41

Recursion

- CS 3358 Data Structures

Big Picture

- Our objective is to write a recursive program and

convince ourselves it is correct with the minimum

amount of effort. - We will put some faith in the principles of

mathematical induction as we write the program - We will then check the program with a small,

non-trivial example - Anything that can be done with a loop, you can do

recursively, but its certainly not appropriate

in many cases. - Does it cause more or less work?

What is recursion?

- Sometimes, the best way to solve a problem is by

solving a smaller version of the exact same

problem first - Recursion is a technique that solves a problem by

solving a smaller problem of the same type

When you turn this into a program, you end up

with functions that call themselves (recursive

functions)

- int f(int x)
- int y
- if(x0)
- return 1
- else
- y 2 f(x-1)
- return y1

All Odd Numbers are Prime

- How a physicist determines that all odd numbers

are prime - 3, 5, 7 are all prime, 9 experimental error,

11, 13 are prime they must all be prime - How a business major determines that all odd

numbers are prime - 3, 5, 7, 9, 11, are all prime they must all be

prime.

The Danger With Testing

- It takes a long time to test a recursive program,

and its easy to make mistakes along the way - //This code doesnt work!!
- bool isPrime(int x)
- if (x 3)
- return true
- else if (isPrime(x-2))
- return true
- else
- return false

Problems defined recursively

- There are many problems whose solution can be

defined recursively - Example n! (n factorial)

1 if n 0 n! (recursive

solution) (n-1)!n if n gt 0

1 if n 0 n! (closed form

solution) 123(n-1)n if n gt 0

Coding the factorial function

- Recursive implementation
- int Factorial(int n)
- if (n0) // base case
- return 1
- else
- return n Factorial(n-1)

(No Transcript)

Coding the factorial function (cont.)

- Iterative implementation
- int Factorial(int n)
- int fact 1
- for(int count 2 count lt n count)
- fact fact count
- return fact

Divide and Conquer

- Recursion works because mathematical induction

works. Have faith. Or. - Imagine that I have lots of very similar

functions - int fact(int n)
- if (n lt 2) return 1
- else return n fact2(n-1)
- int fact2(int n)
- if (n lt 2) return 1
- else return n fact3(n-1)
- // etc, etc.

The Machine is Dumb

- The computer does not realize that the function

is calling itself! - The computer cannot distinguish between fact

calling fact, and fact calling fact2 - After all, the machine code for fact and the

machine code for fact2 are identical - So, if we could just become as dumb as the

computer, recursion would not be confusing.

How is recursion implemented?

- What happens when a function gets called?
- int a(int w)
- return ww
- int b(int x)
- int z,y
- // other statements
- z a(x) y
- return z

What happens when a function is called? (cont.)

- An activation record is stored into a stack

(run-time stack) - The computer has to stop executing function b and

starts executing function a - Since it needs to come back to function b later,

it needs to store everything about function b

that is going to need (x, y, z, and the place to

start executing upon return) - Then, x from a is bounded to w from b
- Control is transferred to function a

What happens when a function is called? (cont.)

- After function a is executed, the activation

record is popped out of the run-time stack - All the old values of the parameters and

variables in function b are restored and the

return value of function a replaces a(x) in the

assignment statement

What happens when a recursive function is called?

- Except the fact that the calling and called

functions have the same name, there is really no

difference between recursive and nonrecursive

calls - int f(int x)
- int y
- if(x0)
- return 1
- else
- y 2 f(x-1)
- return y1

2f(2)

2f(1)

2f(1)

f(0)

f(1)

f(2)

f(3)

Another example n choose k (combinations)

- Given n things, how many different sets of size k

can be chosen? - n n-1 n-1
- , 1 lt k lt n (recursive solution)
- k k k-1
- n n!
- , 1 lt k lt n (closed-form solution)
- k k!(n-k)!
- with base cases
- n n
- n (k 1), 1 (k n)
- 1 n

n choose k (combinations)

- int Combinations(int n, int k)
- if(k 1) // base case 1
- return n
- else if (n k) // base case 2
- return 1
- else
- return(Combinations(n-1, k)

Combinations(n-1, k-1))

(No Transcript)

Recursion vs. iteration

- Iteration can be used in place of recursion
- An iterative algorithm uses a looping construct
- A recursive algorithm uses a branching structure
- Recursive solutions are often less efficient, in

terms of both time and space, than iterative

solutions - Recursion can simplify the solution of a problem,

often resulting in shorter, more easily

understood source code

Termination, Stack Depth and Efficiency

- We should always check to ensure our recursive

programs terminate. - We should avoid programs that have lots of

activation records on the stack at the same time

(the stack depth) - The time required for most recursive programs is

proportional to the number of recursive calls

How do I write a recursive function?

- Determine the size factor
- Determine the base case(s)
- (the one for which you know the answer)
- Determine the general case(s)
- (the one where the problem is expressed as a

smaller version of itself) - Verify the algorithm
- (use the "Three-Question-Method")

Three-Question Verification Method

- The Base-Case Question
- Is there a nonrecursive way out of the function,

and does the routine work correctly for this

"base" case? - The Smaller-Caller Question
- Does each recursive call to the function involve

a smaller case of the original problem, leading

inescapably to the base case? - The General-Case Question
- Assuming that the recursive call(s) work

correctly, does the whole function work

correctly?

Step 1 Identify Your Base Case

- A base case is an input value (or set of values)

for which - Determining the result of our function is

trivially easy - All other values can ultimately be reduced to

(one of) our base case(s) through a finite

sequence of decompositions

Base Case Examples

- Factorial and Fibonacci n lt 2
- Sorting n lt 2 (either array has 1 element or it

has no elements at all) - Towers of Hanoi n 1 (exactly one ring)
- Making Change The amount of change is exactly

the same monetary value as a single coin

Step 2 Pick a Decomposition

- It may take us a couple of tries to get the

decomposition right, but we have to start

somewhere - The decomposition must
- Reduce the problem to a smaller problem
- Result in eventually reaching the base case
- Somehow be useful to solving the original problem

Example Decompositions

- Subtract one from n (and compute n-1 factorial)
- Divide the array in half (and sort each half)
- Skip the first number (and compute the mean of

the remaining elements)???

Recursive binary search

- Non-recursive implementation
- templateltclass ItemTypegt
- void SortedTypeltItemTypegtRetrieveItem(ItemType

item, bool found) - int midPoint
- int first 0
- int last length - 1
- found false
- while( (first lt last) !found)
- midPoint (first last) / 2
- if (item lt infomidPoint)
- last midPoint - 1
- else if(item gt infomidPoint)
- first midPoint 1
- else
- found true
- item infomidPoint

Recursive binary search (contd)

- What is the size factor?
- The number of elements in (infofirst ...

infolast) - What is the base case(s)?
- (1) If first gt last, return false
- (2) If iteminfomidPoint, return true
- What is the general case?
- if item lt infomidPoint search the first half
- if item gt infomidPoint, search the second half

Recursive binary search (contd)

- templateltclass ItemTypegt
- bool BinarySearch(ItemType info, ItemType

item, int first, int last) - int midPoint
- if(first gt last) // base case 1
- return false
- else
- midPoint (first last)/2
- if(item lt infomidPoint)
- return BinarySearch(info, item, first,

midPoint-1) - else if (item infomidPoint) // base

case 2 - item infomidPoint
- return true
- else
- return BinarySearch(info, item,

midPoint1, last)

Recursive binary search (contd)

- templateltclass ItemTypegt
- void SortedTypeltItemTypegtRetrieveItem

(ItemType item, bool found) - found BinarySearch(info, item, 0,

length-1)

Recursive InsertItem (sorted list)

location

location

location

location

Recursive InsertItem (sorted list)

- What is the size factor?
- The number of elements in the current list
- What is the base case(s)?
- If the list is empty, insert item into the empty

list - If item lt location-gtinfo, insert item as the

first node in the current list - What is the general case?
- Insert(location-gtnext, item)

Recursive InsertItem (sorted list)

- template ltclass ItemTypegt
- void Insert(NodeTypeltItemTypegt location,

ItemType item) - if(location NULL) (item lt

location-gtinfo)) // base cases - NodeTypeltItemTypegt tempPtr location
- location new NodeTypeltItemTypegt
- location-gtinfo item
- location-gtnext tempPtr
- else
- Insert(location-gtnext, newItem) //

general case - template ltclass ItemTypegt
- void SortedTypeltItemTypegtInsertItem(ItemType

newItem) - Insert(listData, newItem)

- No "predLoc" pointer is needed for insertion

location

Recursive DeleteItem (sorted list)

location

location

Recursive DeleteItem (sorted list) (cont.)

- What is the size factor?
- The number of elements in the list
- What is the base case(s)?
- If item location-gtinfo, delete node pointed

by location - What is the general case?
- Delete(location-gtnext, item)

Recursive DeleteItem (sorted list) (cont.)

- template ltclass ItemTypegt
- void Delete(NodeTypeltItemTypegt location,

ItemType item) - if(item location-gtinfo))
- NodeTypeltItemTypegt tempPtr location
- location location-gtnext
- delete tempPtr
- else
- Delete(location-gtnext, item)
- template ltclass ItemTypegt
- void SortedTypeltItemTypegtDeleteItem(ItemType

item) - Delete(listData, item)

Recursion can be very inefficient is some cases

15

Deciding whether to use a recursive solution

- When the depth of recursive calls is relatively

"shallow" - The recursive version does about the same amount

of work as the nonrecursive version - The recursive version is shorter and simpler than

the nonrecursive solution