1 Recursion How to design an Object Oriented program Lecture
#8
Slide 2
2 Recursion! Building a SuDoKu solver! Even cracking codes and
ciphers! Recursion is one of the most difficult topics in Computer
Science But once you master it, you can solve all sorts of cool
problems! Writing a computer program that can play chess or
checkers!
Slide 3
3 Idea Behind Is the problem trivially solved Break the problem
into two or more simpler sub-problems Just return the answer Solve
each sub-problem j Collect all the solution(s) to the sub-problems
Use the sub-solutions to construct a solution to the complete
problem Return the solution by calling some other function on the
sub-problem j Yes No by calling ourself!
SomeOtherFunction(sub-problem 1 ) Problem SolvingRecursion
SomeOtherFunction(sub-problem n )... SolveAProblem(problem)
SolveAProblem(sub-problem 1 ) SolveAProblem(sub-problem n )
Slide 4
4 The Lazy Persons Sort Lets design a new sorting algorithm,
called the lazy persons sort Lazy Persons Sort: Split the cards
into two roughly-equal piles Hand one pile to nerdy student A and
ask them to sort it Hand the other pile to nerdy student B and ask
them to sort it Take the two sorted piles and merge them into a
single sorted pile The input to this sort are a bunch of index
cards with #s. 62217395146221739514
Slide 5
5 The Lazy Persons Sort Lazy Persons Sort: Split the cards into
two roughly-equal piles Hand one pile to nerdy student A and ask
them to sort it Hand the other pile to nerdy student B and ask them
to sort it Take the two sorted piles and merge them into a single
sorted pile 9522171463 Pretty good! All I had to do was merge two
piles of sorted cards! (My nerdy students did all the real work!)
Well that worked so well, I think Ill have them sort the other six
hundred! 1761492277499322 That sucks. Sorting 3 cards was OK but
300? I dont know where to start! Yeah right. Hey, youre kind of
cute when youre angry! Thanks But what can we do? I think I have an
idea. We can be lazy too, lets change Careys algorithm just a
bit.
Slide 6
6 The Lazy Persons Sort Lazy Persons Sort: Split the cards into
two roughly-equal piles Hand one pile to nerdy student A and ask
them to sort it Hand the other pile to nerdy student B and ask them
to sort it Take the two sorted piles and merge them into a single
sorted pile 1761492277499322 hot say do the Lazy Persons Sort Oh
yeah One more thing. Youre a genius! So now all each person has to
do is split their pile in two, hand each one to someone else and
then finally merge the results! Correctamundo. And no one person
has to do any complex sorting! Isnt this some kind of Pyramid
scheme? Will it work? Well, it worked for Carey. Why cant we use
exactly the same process he did with our piles? And the students we
give each half of our piles to can do the same thing too! Then we
can blow this joint and go play StarCraft! Very clever, students.
But your approach has one flaw, can you see it? I think I see it
The algorithm isnt complete What happens when a person ends up with
just a single notecard. The algorithm breaks down. How can you
split a single card into two equal piles?!!? Excellent, Mr.
Smallberg. And do you have a solution? I think so. If you just
rewrite it a bit
Slide 7
7 Split the cards into two roughly-equal piles Hand one pile to
nerdy student A and ask them to sort it Hand the other pile to
nerdy student B and ask them to sort it Take the two sorted piles
and merge them into a single sorted pile The Lazy Persons Sort
Split the cards into two roughly-equal piles Hand one pile to nerdy
student A and ask them to sort it Hand the other pile to nerdy
student B and ask them to sort it Take the two sorted piles and
merge them into a single sorted pile 1761492277499322 hot say do
the Lazy Persons Sort If youre handed just one card, then just give
it right back. Lazy Persons Sort: Oh yeah. And one more thing.
studly Ah, I see. When a person at the bottom of the pyramid gets a
single card, they cant split it in half Besides its just one card,
so its technically already sorted... So they just hand it back to
the guy above them and let them merge it. Correct! Amazing, huh? By
having an algorithm use itself over and over, you can solve big
problems!
Slide 8
8 The Lazy Persons Sort Split the cards into two roughly-equal
piles Hand one pile to person A and say do the Lazy Persons Sort
Hand the other pile to person B and say do the Lazy Persons Sort
Take the two sorted piles and merge them into a single sorted pile
If youre handed just one card, then just give it right back. Lazy
Persons Sort: The Lazy Persons Sort (also known as Merge Sort) is a
perfect example of a recursive algorithm! Every time our MergeSort
function is called, it breaks up its input into two smaller parts
and calls itself to solve each sub-part. Its hard to believe it
works! Ok but would you agree it definitely works if we change the
algorithm like this? If we can rely upon OtherSort to somehow
properly sort each sub-array, we agree that our udpated MergeSort
will work. Correct? void MergeSort(an array) { if (arrays size ==
1) return; // array has just 1 item, all done! MergeSort( first
half of array ); // process the 1 st half of the array MergeSort(
second half of array); // process the 2 nd half of the array
Merge(the two array halves); // merge the two sorted halves // now
the complete array is sorted } OtherSort Ok, well let me show you
the way OtherSort works void OtherSort(an array) { // just call
merge sort! MergeSort( array ); } When you write a recursive
function Your job is to figure out how the function can use itself
(on a subset of the problem) to get the complete problem solved.
When you add the code to make a function call itself, you need to
have faith that that call will work properly (on the subset of
data). It takes some time to learn to think in this way, but once
you get it, youll be a programming Ninja! If youre willing to
assume that MergeSort actually works on a full array of data Then
you should be willing to have faith that itll work on half an array
of data too!
Slide 9
9 The Two Rules of Recursion RULE ONE: The Stopping Condition
(aka Base Case): Your recursive function must be able to solve the
simplest, most basic problem without using recursion. Every
recursive function must have a stopping condition! Remember: A
recursive function calls itself. Therefore, every recursive
function must have some mechanism to allow it to stop calling
itself.
Slide 10
10 void eatCandy(int layer) { if (layer == 0) { cout val ); int
rest = 53 Step #6: Validating our Function int main() { } Again,
start by testing your function with the simplest possible input.
Node *head = nullptr; // empty list cout next == nullptr ) // the
only node return cur->val; // so return its value biggest(
cur->next ); {} {} biggest( )int Node *cur return max ( rest,
cur->val ); int rest = 42 So our next simplest input is a list
with a single node. Lets validate our function on such a list. Our
next simplest input is a list with two nodes. Lets validate our
function on such a list. And this is the correct result! CHECK!
Each time our function runs, its supposed to return the biggest
value of the list that was passed in to it. (Our function has no
idea that its looking at the tail-end of a longer linked list it
just sees the list starting at cur.) Since the list pointed to by
cur only has one node, by definition, that one node must hold the
biggest value in the list! So our func returns its value. 200 42 52
In this case, the first/top nodes value (52) is larger than the
biggest value from the rest of the list (42). So our function
returns the first nodes value as the biggest. In this case,
cur->next == nullptr indicating that this is the only node in
the linked list.
Slide 54
54 main() { Node *head;... // create linked list cout next ==
nullptr) return(cur->val); int rest = biggest( cur->next );
return max( rest, cur-val ); } 1200 cur Biggest-in-List
Trace-through int biggest(Node *cur) { if (cur->next == nullptr)
return(cur->val); int rest = biggest( cur->next ); return
max( rest, cur-val ); } cur 1300 int biggest(Node *cur) { if
(cur->next == nullptr) return( cur->val); int rest = biggest(
cur->next ); return max( rest, cur-val ); } 1400 cur 8 8 12
3
Slide 55
55 Recursion Challenge #3 Write a recursive function called
count that counts the number of times a number appears in an array.
main() { const int size = 5; int arr[size] = {7, 9, 6, 7, 7}; cout
next, val ); if (cur->value == val) return 0; // # found in top
node int posInRestOfList = if (posInRestOfList == -1) return -1; //
# was not in tail Write a function that finds and returns the
earliest position of a number in a linked list. If the number is
not in the list or the list is empty, your function should return
-1 to indicate this. else return posInRestOfList + 1; void
magicfindPos(Node *n, int v) {} findPos(cur->next, val); magic
Node *head1 = nullptr; // empty cout
63 Recursion: Binary Search Albert Brandy Carol David Eugene
Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] =
{Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1);
// Value not found else { int Mid = (top + bot) / 2; if (f ==
A[Mid]) return(Mid); // found return where! else if (f < A[Mid])
return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A,
Mid + 1,bot,f)); } As our binary search progresses, top will get
bigger and bot will get smaller. If they ever pass each other, it
means that our item was not in the array. (0 + 10) / 2 Which is 5
David == Frank 0 1 2 3 4 5 6 7 8 9 10 David < Frank top bot
Mid
Slide 64
64 Recursion: Binary Search Albert Brandy Carol David Eugene
Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] =
{Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1);
// Value not found else { int Mid = (top + bot) / 2; if (f ==
A[Mid]) return(Mid); // found return where! else if (f < A[Mid])
return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A,
Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) {
if (top > bot) return (-1); // Value not found else { int Mid =
(top + bot) / 2; if (f == A[Mid]) return(Mid); // found return
where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if
(f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } 4 0 bot Mid 0 >
4 (0 + 4) / 2 Which is 2 David == Carol David < Carol Mid David
> Carol
Slide 65
65 Recursion: Binary Search Albert Brandy Carol David Eugene
Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] =
{Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1);
// Value not found else { int Mid = (top + bot) / 2; if (f ==
A[Mid]) return(Mid); // found return where! else if (f < A[Mid])
return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A,
Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) {
if (top > bot) return (-1); // Value not found else { int Mid =
(top + bot) / 2; if (f == A[Mid]) return(Mid); // found return
where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if
(f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } int BS(string A[],
int top, int bot, string f) { if (top > bot) return (-1); //
Value not found else { int Mid = (top + bot) / 2; if (f == A[Mid])
return(Mid); // found return where! else if (f < A[Mid])
return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A,
Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) {
if (top > bot) return (-1); // Value not found else { int Mid =
(top + bot) / 2; if (f == A[Mid]) return(Mid); // found return
where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if
(f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } 3 4 3 > 4 (3 +
4) / 2 Which is 3 David == David bot top Mid 3
Slide 66
66 Recursion: Binary Search Albert Brandy Carol David Eugene
Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] =
{Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1);
// Value not found else { int Mid = (top + bot) / 2; if (f ==
A[Mid]) return(Mid); // found return where! else if (f < A[Mid])
return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A,
Mid + 1,bot,f)); } int BS(string A[], int top, int bot, string f) {
if (top > bot) return (-1); // Value not found else { int Mid =
(top + bot) / 2; if (f == A[Mid]) return(Mid); // found return
where! else if (f < A[Mid]) return(BS(A,top,Mid - 1,f)); else if
(f > A[Mid]) return(BS(A, Mid + 1,bot,f)); } bot top Mid 3
Slide 67
67 Recursion: Binary Search Albert Brandy Carol David Eugene
Frank Gordon Grendel Hank Wayne Yentle main() { string names[11] =
{Albert,}; if (BS(names,0,10,David) != -1) cout bot) return (-1);
// Value not found else { int Mid = (top + bot) / 2; if (f ==
A[Mid]) return(Mid); // found return where! else if (f < A[Mid])
return(BS(A,top,Mid - 1,f)); else if (f > A[Mid]) return(BS(A,
Mid + 1,bot,f)); } 0 1 2 3 4 5 6 7 8 9 10 top bot Mid 3
Slide 68
68 Recursion Helper Functions So we just saw a recursive
version of Binary Search: Notice how many crazy parameters it
takes? What is top? Whats bot? Thats going to be really confusing
for the user! int BS(string A[], int top, int bot, string f) {... }
Wouldnt it be nicer if we just provided our user with a simple
function (with a few, obvious params) and then hid the complexity?
int SimpleBinarySearch(string A[], int size, string findMe) { }
This simple function can then call the complex recursive function
to do the dirty work, without confusing the user. And then write a
helper function to actually do the complex recursion with complex
parameters return BS(A, 0, size-1, findMe); In these cases, youll
want to define a function with a simple interface
(earow-to-understand parameters) You can then call your helper
function from your simple function
Slide 69
69 Solving a Maze We can also use recursion to find a solution
to a maze. In fact, the recursive solution works in the same basic
way as the stack-based solution we saw earlier. The algorithm uses
recursion to keep moving down paths until it hits a dead end. Once
it hits a dead end, the function returns until it finds another
path to try. This approach is called backtracking
Slide 70
70 Solving a Maze void solve(int row, int col) { m[row][col] =
#; // drop crumb if (row == drow && col == dcol) solvable =
true; // done! if (m[row-1][col] == ' ) solve(row-1,col); if
(m[row+1][col] == ' ) solve(row+1,col); if (m[row][col-1] == ' )
solve(row,col-1); if (m[row][col+1] == ' ) solve(row,col+1); } bool
solvable; // globals int dcol, drow; char m[11][11] = {
"**********", "* *", "* * * ** *", "* *** *", "* * * *", "* *****
*", "* * *", "********** }; main() { solvable = false; drow = dcol
= 10; solve(1,1); if (solvable == true) cout
92 Use Case #1 1. The user wants to add an appointment to their
calendar. A.The user creates a new Appointment object and sets its
values: Appointment *app = new Appointment;
app->setStartTime(10am); app->setEndTime(11am); B.The user
adds the Appointment object to the Calendar: Calendar c;
c.addAppointment(app); It looks like were OK here. Although it
might be nicer if we could set the Appointments values during
construction Appointment bool setStartTime(Time &st) bool
setEndTime(Time &st) bool addParticipant(string &user) bool
setLocation(string &location) Appointment() ~Appointment()
private: Time m_startTime; Time m_endTime; string
m_participants[10]; string m_location; Time &start, Time
&end, string loc, string parts[]) (10am,11am,Dodd,);
Slide 93
93 Use Case #2 2. The user wants to determine if they have an
appointment at 5pm with Joe. Hmm Can we do this with our classes?
Calendar list getListOfAppts(void) bool addAppt(Appointment *addme)
bool removeAppt(string &apptName) bool checkCalendars(Time
&slot, Calendar others[]) bool login(string &pass) bool
logout(void) Calendar() and ~Calendar() private: Appointment
m_app[100]; String m_password; It doesnt look like we can find if
we have an appointment at a particular time Lets add this!
Appointment *checkTime(Time &t) Calendar c;... Appointment
*appt; appt = c.checkTime(5pm); if (appt == NULL) cout
isAttendee(Joe)) cout