Chapter 6 - Recursion
Chapter 6 - Recursion
1
Final Exam
In case anyone’s making travel plans now…
Wednesday, December 10
12 noon – 3 PM
Location: TBA
2
Recursion
Definition
A programming technique where a function calls itself
Very effective technique in programming
Let’s solve triangle(n) in this way, where n is the row of the triangle:
triangle(1) = 1
triangle(2) = 3
triangle(3) = 6
triangle(4) = 10
5
Triangular Numbers and Recursion
Note:
triangle(1) = 1
triangle(2) = 3
triangle(3) = 6
triangle(4) = 10
We can note that, in general:
triangle(n) = n + triangle(n-1)
This can be our recursive step
Which will carry us to the base case:
triangle(1) = 1
This cannot be broken down any further
6
Triangular Numbers and Recursion
Here’s our recursive solution:
triangle(1) = 1, triangle(n) = n + triangle(n-1)
In Java:
7
Scope
In Java, each function
call creates a new
‘scope’
Each of which declares
a new version of n,
which is visible
Suppose we call
triangle(5)
public int triangle(int n) {
if (n == 1) return 1;
else return n + triangle(n-1);
}
8
Recursive Methods
Characteristics:
They call themselves
When they call themselves, they do so to solve a smaller problem
A base case must exist
What happens if it doesn’t?
9
Iteration
Anything recursive can be made iterative, using a while or for loop:
int triangle(int n) {
int total = 0;
while (n > 0) {
total += n;
n--;
}
}
10
Efficiency of Recursion
Recursion is very often simpler and easier to read
But, often is slower. Why?
Overhead of function calls
Sometimes, you can make redundant recursive calls
We’ll see an example of this with Fibonacci
11
Mathematical Induction
This can be a convenient way to represent a recursive problem:
tri(n) = { 1 n=1
{ n*tri(n-1) n>1
12
Example: Factorials
Let’s start by representing fact(n) by mathematical induction:
13
Factorial Scope
In Java, each function
call creates a new
‘scope’
Each of which declares
a new version of n,
which is visible
Suppose we call
factorial(4)
public int factorial(int n) {
if (n == 0) return 1;
else return n * factorial(n-1);
}
14
Fibonacci Numbers
Mathematical Induction:
fib(n) = {1 n=0
{1 n=1
{fib(n-1) + fib(n-2) n>1
16
Binary Search
Array has values 1-100
First search: Check element 50
50 > 33, so repeat on first half (1-49)
Second search: Check element 25
25 < 33, so repeat on second half (26-49)
Third search: Check element 37
37 > 33, so repeat on first half (26-36)
Fourth search: Check element 31
31 < 33, so repeat on second half (32-36)
Fifth search: Check element 34
34 > 33, so repeat on first half (32-33)
Sixth search: Check element 32
32 < 33, so repeat on second half (33)
Seventh search: Check element 33! Found.
17 So 7 comparisons. With linear search, it would’ve been 33.
Our implementation before was iterative…
public int find(long key) {
int lower = 0;
int upper = nElems-1;
int curIn;
while (true) {
curIn = (lower + upper) / 2;
if (a[curIn] == key) return curIn;
else if (lower > upper) return -1;
else {
if (a[curIn] < key) lower = curIn + 1;
else upper = curIn – 1;
}
}
18 }
But we can also do it recursively!
If we think of binary search in these terms:
Start lower at 0, and upper at n-1
Let mid = arr[lower+upper]/2
If arr[mid] = key, return mid # we’re done
else if lower > upper, return -1 # not found
else if arr[mid] > key:
perform binarysearch on arr[lower…mid-1]
else if arr[mid] < key:
perform binarysearch on arr[mid+1…upper]
So we have TWO base cases. One if the element is found, and the other if
19 it is not found.
Using this pseudocode, let’s do it in Java…
Start lower at 0, and upper at n-1
Let mid = arr[lower+upper]/2
If arr[mid] = key, return mid # we’re done
else if lower > upper, return -1 # not found
else if arr[mid] > key:
perform binarysearch on arr[lower…mid-1]
else if arr[mid] < key:
perform binarysearch on arr[mid+1…upper]
22
Anagrams
Involves producing all possible combinations of the letters of a word.
Example: Anagram the word cat:
cat
cta
atc
act
tca
tac
Six possible combinations
23
Anagrams: General
In general, for a word of n letters, there will be n! combinations assuming
all letters are distinct
We saw for cat (3 letters), there were 6 possible
If some letter(s) repeat themselves, this will reduce the number of
combinations. Example, tat only has 3:
tat
att
tta
24
Anagram Algorithm
Anagram a word with n letters:
Anagram the rightmost n-1 letters
If n=2, display the word
Rotate all n letters
Repeat these steps n times
26
rotate() and doAnagram() function
Java implementation, page 266
We will write:
A rotate() function which moves each character one slot to the left, and the
first character in the last position
A recursive anagram() function which invokes rotate().
Base case: n=1, just return
Recursive step (do n times):
anagram(n-1)
display if n=2
rotate(n)
27
Output Produced
cats cast ctsa ctas csat csta
28
Towers of Hanoi
An ancient puzzle consisting of disks on pegs A, B and C
Start all disks on peg A
31
Similar for moving top
So…
# A is source peg
# B is intermediate peg
# C is destination peg
Solving
TOH(n,A,B,C):
Solve TOH(n-
1,A,C,B)
Move nth to C
Solve TOH(n-
1,B,A,C)
32
Base Case?
For TOH(n,A,B,C):
Well if there’s just one
disk (n=1), move from
A to C!
Java implementation,
page 278
Note: It’s just a few
lines!
33
Complexity
For n disks:
(n disks) - 1st call, 2 recursive calls (n disks)
(n-1 disks) Two 2nd calls, 2 recursive calls
(n-2 disks) Four 3rd calls, 2 recursive calls
…
(1 disk) Many nth calls, base case
Let’s draw the tree
See why, this is too expensive for large numbers of disks?
Old legend: In remote India temple, monks continuously work at solving this
problem with 64 disks and 3 diamond towers
The world ends when they are finished
No worries, it will take forever anyway….
34
Number of Operations
Each recursive call generates two recursive calls, and a constant number
of operations (call it c)
First call: c
Two second calls, times c: 2*c
Four third calls, times c: 4*c
…
2n nth calls, times c: 2n*c
36
Merging
Let’s say we have an array of size n
Suppose we used recursion and solved a smaller problem (why would we
ever do that? I don’t know ) and had two sorted subarrays:
37
Merge..
Have: two subarrays, and a temporary array of size n
Compare first elements
Take the lesser, and put it in the temporary array
38
Merge…
Whichever one we chose, move one spot to the right in that subarray and
repeat
39
Keep going…
40
And going…
41
And going…
42
And going…
43
Get the idea?
44
Few more…
45
Put in the rest…
When we get to the end of one subarray, just insert the rest of the other.
46
Finally…
We’re done when the temporary array is full
47
So now, we know…
If we have two sorted subarrays, we can merge them to sort the entire
array. And we can do it in O(n) time.
Just one comparison for each of the n elements
48
Pictorally
…
49
So conceptually, what must we do?
mergesort(A, n): # Sort an array of size n
mergesort(first half of A, n/2)
mergesort(second half of A, n/2)
merge(first half of A, second half of A)
50
Let’s add a merge() procedure to this class. (p.
289)
class DArray {
private long[] theArray;
private int nElems;
public Darray(int max) {
theArray = new long[max];
nElems++;
}
public void insert(long value) {
theArray[nElems] = value;
nElems++;
51 }
What merge() accepts
A workspace array of size n
The lower, middle and upper indices of theArray to merge
First half is index lower to index (middle-1)
Second half is index middle to index upper
So n=upper-lower+1
52
Now write mergesort()
Our recursive mergesort will accept:
Workspace of size n, and lower/upper indices of theArray to sort
Initial call will pass an empty workspace, 0, and nElems-1.
53
Complexity
Every call makes two recursive calls, each with n/2 copies
First call: n copies, and generates:
Two recursive calls at (n/2) copies each, which generate:
Four recursive calls at (n/4) copies each
…
n recursive calls at (n/n) copies each
54
Total number of operations
n + 2(n/2) + 4(n/4) +…. + n(n/n)
= n + n + …. + n
= (log n + 1) * n
= n log n + n
O(n log n)
Best so far!!
55