Announcements
 Last lecture before the exam!!
 In two weeks, cause no class next week
 We will have a short (20 minute) review immediately before the exam
 I will try to give you some study materials/ideas of what the exam will be like in the next couple weeks
 Feedback on lecture quiz answers
 Be sure to answer all parts of a question
 Be sure your answer is at the right level of abstraction
 If I ask about something general, don’t explain something concrete (except as an example)
 “Call” and “return” are very specific terms in programming
 Call is when program control transfers to another method/function
 Return is when program control transfers back to the method that called this one
 You cannot call variables, classes etc.
DivideandConquer
Really shoulda talked about this last time :P
 We can solve some problems by splitting them into subproblems that are a fraction of the original size
 Each subproblem can then be split further into smaller fractions
 Then we can combine the results of the subproblems to get a final result
 Going back to integer exponentiation
 Iterative definition was “multiply product by B, N times”

Recursive definition was:
 Remember that recursion is just another way to write a loop
 So does this solve the problem any faster?
 No, we still recurse N times
 Each subproblem is just a constant size smaller 
 How would we make each subproblem a fraction of N?
 Defining it in a divideandconquer way
 Remember this rule of exponentiation:
 How do we divide each step?
 If we have , that’s
 But the two halves of the computation look the same
 Why bother doing twice?
 Why not do that once and then multiply it by itself?
 Multiplying something by itself is also known as squaring
 This becomes our combine step.
 So the idea is…
 To do , do , and then square that.
 What if N is odd?
 Then we have to take the “extra 1” out of the exponent, leaving us with an even number
 And finally, what is the base case?
 Just like last time,

Putting it all together…
 What is the runtime of this?
 Each step involves cutting the problem in half.
 If we start at , then we’d to 16, 8, 4, 2, 1, 0
 We’re kind of answering: what power do we raise 2 to, to get N?
 That’s a logarithm!
 So it’s
 Could you write this iteratively?
 Yes, absolutely.
 But honestly, it’d be kind of awkward.
 Another divideandconquer example: binary search
 You know how to search an array linearly (look at every item in order)
 But if we have an array that is in sorted order, we can find things much faster.
 Think about a dictionary. Do you start searching at the beginning?
 No. You kind of “weed out” portions of the book where the word can’t be.
 This is the idea behind binary search:
 Split the array into two halves with one value between them
 Look at that one value
 If it’s the value you’re looking for, you’re done.
 If it’s bigger than the value you’re looking for, look in the smaller side.
 If it’s smaller than the value you’re looking for, look in the bigger side.
 If it’s not in either side, it’s not in the array.
 And if the array is empty, it’s not in the array.

Let’s say we’re looking for 17 in this array:

Split the array into two (roughly) equal halves with one value between:
 That middle value is not what we’re looking for, but it’s bigger. So can 17 be in the right half?
 NO!

Repeat the process with the left (smaller) half. (Let’s split the difference by making the left side bigger.)

Now 9 is too small. Repeat with the right half.
 There it is!
 If I were looking for 18, I’d look in the right side… but the right side is empty, so it would not be found.
 Again, we’re throwing out half the problem on each step.
 So again, this is .
 And again, you can do this iteratively.
 It’s a little tricky, but possible.
Multiple recursion
 So far, the recursive problems we’ve looked at only require one recursive step each time
 Both exponentiation methods do one recursive exponentiation in each case
 Binary search only looks in one half of the array in each case
 But some problems require doing multiple recursive calls in at least one case
 Suppose we wrote a recursive search for an unsorted array
 We could do the same “split in half, look at the middle item” approach…
 But since the array is unsorted, we’re forced to look in both halves of the array
 Granted this is a silly way of doing it, but it illustrates the concept
 We’ll come back to multiple recursion later when we discuss trees
 And we’ll see where recursion really shines
 Suppose we wrote a recursive search for an unsorted array
Tail recursion
 Singlyrecursive functions can be trivially converted into loops
 Essentially they are using the call stack as temporary storage
 An implicit stack data structure! Built into the program!
 Some languages/compilers (not Java…) can recognize this
 And instead of nesting another function call…
 They replace the current function call with the new one
 This prevents call stack overflows
 You can recurse an infinite number of times and the call stack will stay the same size.
 This is restricted to recursive calls of the form:
return func(...)
 If you do anything after the recursive call, it’s not possible:
return func(...) + 1
 Why is this important?
 Well… our computers have coevolved with imperative, iterative programming languages
 They’re really good at running loops quickly!
 They’re not as good at running recursive functions.
 Every function call pushes a good deal of data onto the call stack.
 Even in a tailrecursive call, we’re essentially doing a pop followed by a push.
 This takes a lot more time and space than just running a loop.
 So is recursion useless, if we can just rewrite everything as loops?
 Well, no.
 Again: it’s a problemsolving tool.
 Having multiple tools in your repertoire is useful.
 Some problems are just easier to express recursively.
 And some multiplyrecursive functions are very difficult to write as loops.
 You have to get a separate Stack or Queue (ooo) involved.
Backtracking
 The last recursion topic!
 And also what you’ll need to know for project 3…
 The book and everyone and their brother uses this “Eight Queens” problem as an example but
 idk
 I never played chess
 It seems like a pointless problem to solve
 Instead let’s consider this: given a money value, list all the different ways you can combine coins and bills to represent that value.
 It’s gonna be a lot, isn’t it?
 One possible “solution” to this problem is gonna be a list of values, where each item in the array is “how many of that coin/bill do you need?”
 If we only had coins (pennies, nickels, dimes, quarters) and wanted to list the ways we could represent 10 cents:
 (10 pennies)
 (5 pennies, 1 nickel)
 (2 nickels)
 (1 dime)
 An invalid solution would be like
 That’s more than 10 cents.
 If we only had coins (pennies, nickels, dimes, quarters) and wanted to list the ways we could represent 10 cents:
 Let’s think about how we might solve this:
 Start at .
 Increase the number of pennies until we get to the “goal”.
 Stop increasing the number of pennies. Stop it.
 Then back up
 Take the pennies away until we can increase the number of nickels
 Once we find a solution involving at least one nickel…
 back up again.
 Eventually, we’ll find all the solutions.
 The backtracking template

Every backtracking problem will follow this basic template:
void solveProblem(currentAttempt) { if(currentAttempt is a dead end) return; if(currentAttempt is a valid solution) output currentAttempt; for(each possible nextAttempt based on currentAttempt) solveProblem(nextAttempt) }
 The parts work like this:
 is a dead end means “this attempt is invalid, and any solution based on it would be invalid.”
 e.g. if we’re looking for 10 cents and we get 12, there’s no way to add more coins and get 10 cents.
 is a valid solution means “this attempt solves the problem.”
 e.g. we found a combination of coins/bills that totals to 10 cents.
 a next attempt based on the current attempt means that we modify the current attempt in some way to get “closer” to the goal.
 e.g. we only have 4 cents, and want to get to 10, so we add one coin/bill.
 in the coin problem, once we find a solution, there are no “next attempts” so this loop will not run.
 is a dead end means “this attempt is invalid, and any solution based on it would be invalid.”
 Since we can make many recursive calls, this is multiplyrecursive
 Trying to solve a problem like this iteratively would be… nightmarish.
 The “backing up” happens implicitly
 Whenever we return from a recursive call, we’re “backing up” to a previous attempt
 You will go into this technique more deeply in 1501
