 Well, again, so let's talk more about designing and tracing recursive algorithms to solve problems. I'm in my recursion worksheet again, and now I'm on example two. And this problem asks us to figure out if a string S is a palindrome. Okay, so what's a palindrome? A palindrome is a word or a string that is the same if you read forwards or backwards. Okay, left to right or right to left. So how do you detect that? Well, the easiest way is you just kind of look at each the characters at the beginning and at the end and you say, are they the same thing? And if so, all right, how about the next characters in? Right, so we would maybe look at these characters first and then these characters second. And we just kind of keep walking inward and we ask ourselves, hey, are these guys the same as we keep stepping in? And if so, then we have a palindrome, right? Because characters are the same, the string can mirror itself and it's going to read the same way. Okay, so let's design a recursive solution to answering this question. So what we want to do is return true if the string is a palindrome and return false otherwise. So let's think of the base case. The base case, in this case we're actually going to have two base cases, is an answer. It's a case where you have a very, very, very small problem here. So not all these characters up here, but it's a small problem that gives you the answer. So that small problem is going to have to do with what? What is the smallest string that you would consider to be a palindrome? Well, what's the smallest string you can have? The smallest string you can have is length zero. Is an empty string a palindrome? I say yes, right? I say yes, right? Not only because I know the answer to this, but empty is empty, right? It's the same forward and backward, that's what a palindrome is. Excuse me, there is a nat flying around in front of me. I'll try not to swallow it. So what's the base case? When one of them is when, that's annoying hearing yourself, I have to say. I was not expecting that, sorry. One of the base cases is when the length of s is equal to zero. Okay, so when the length of s is equal to zero, we are going to return true. An empty string is a palindrome. There's another base case. The base case when the string has length one, right? If you have one character, it's the same. That string is the same forward or backward. So when the length of the string is equal to one, that is also, we're going to return true, okay? So in this case, we actually have two base cases. That's totally fine. You can have two base cases. Just means that you've got an extra component to your if statement in your code. No problem. A lot of recursive algorithms have two base cases. All right, how about the non-base case? What's our recursive step that makes progress toward the base case? Well, to make progress toward the base case, we need to chop this string down, okay? So what's our question going to be? Well, if, you know, one way to look at this again, kind of what I talked about up here a second ago, if the characters at the ends of the string are the same, then potentially you have a palindrome, right? You have to make sure the characters at the ends of the string are the same, and then what? Well, the recursive step says, look at all the characters inside there and ask the question, is everything inside there a palindrome, right? So what we'll do is we'll ask the question, is the first character in the string, right? At index zero equal to the last character in the string, right? Index minus one is going to be the last character of our string. And what, right? Well, we're comparing these two, the first and the last, and what we want to check, though, if those two things are equal and all the rest is equal, okay? Well, how do I know if the rest is equal? I call palindrome my recursive function on that inner part. And how can we do that? Well, we can use the string slicing operator to give us that, right? So the string slicing operator, if you don't remember, you give it a bracket and you give it the starting index, which in this case is one. We want to start at index one, this guy, the B, and go all the way up to but not including the stop index, right? So the stop index will be minus one, the last item. The stop index is not included in a slice operator, okay? So here's our recursive call. This is the recursive step that makes progress toward the base case, right? So how will this execute? Here's an example. Let's call print of the palindrome stunts, okay? So what is this call? All right. Well, when palindrome is first called, what we have in here is the string stunts. Okay. Now, what is this palindrome return? Well, let's look at our pseudocode. The length of S is not zero and it's not one, okay? So then it asks the question, all right, is S sub zero equal to S sub one? So this is S sub zero and this is sub minus one, excuse me. Yeah, they are equal, okay? So it's gonna return, is S sub zero equal to S sub negative one? Yes, it's gonna return true and what? Well, we don't know yet, right? And the result of calling the palindrome on the smaller list, okay? So now we call palindrome again. So what is S now, okay? We've called palindrome with S sub one to S negative one exclusive, right? So that's this, tunt, right? If you're an Archer fan, you know who this is, right? All right. So now we run our palindrome code. The length of this is not zero, it's not one. So what do we do? We compare S sub zero. S has shrunk here, right? It's the S in the context of the current call to palindrome. S has shrunk down. Now we're just looking at the inner part of stunts, right? S sub zero is t. S sub negative one, the last item is also a t. So we are gonna return true again, right? This part, true and we don't know yet, okay? We haven't hit a base case. We're gonna make this recursive call, okay? So once again, we call palindrome. What's S this time, okay? Well, we take, according to our palindrome definition up here, we take the slice of S from one to negative one. That's gonna give us U N, okay? All right, go to our code. What does our code say? What's the length? Length is not zero, length is not one. Does S sub zero equal S sub minus one? No, of course not. These things are false, okay? So false, I'm gonna return false and what? Well, what do we know about Python? Python's gonna see that you're saying false and something, right? And you hopefully learned in 131 of the concept called short circuit evaluation. So when Python sees false and, right? This is a Boolean expression. False anded with anything is false, right? So Python knows there is no point in continuing this call. It stops right here. False and anything is false. So I can just return false here. In my job, palindrome of un is done, okay? Now palindrome being called with tunt gets true and false. True and false, I know the answer to that. True and false is false. False and anything is false, okay? Palindrome of tunt is done. And now palindrome of stunts can finish, right? True and false is still once again false. So finally, it is gonna return false here, okay? So this is how it evaluates. We'll switch over to the code here in a second and take a look at that. But before we go there, let's move on to binary search, okay? So our binary search algorithm, this was Mario jumping on the coins looking for the mushroom. And when we gave the binary search algorithm in the search worksheet, that was an iterative solution. We had a while loop in there where we had variables that were keeping track of a first item, a last item in a mid, and we kept checking the mid and moving the first and the last, kind of chopping it down, dividing and conquering until we either found the item at the middle point or the last crossed over the first and we knew that it wasn't in the list, okay? Binary search for using a recursive solution is maybe a little more elegant than the iterative solution, okay? So binary search, how do you know? Let's think first again about the base cases. All right, so we're gonna search in our algorithm. We're looking for a target value, that's the thing we're looking for, and we're searching a list here, okay? So how do we know? We're gonna keep chopping this problem down. Again, go back to the first video and think about Mario jumping in this size of the platform getting smaller and smaller and smaller. Well, how does he know when the mushroom isn't there? How do you know when the value is not in the list? You know when the value is not in the list if the list is empty, okay? You keep chopping down your search. You keep making the platform smaller and smaller and smaller and you never find the mushroom. It must not be there, right? You know it's not there when you've run out of blocks to check, okay? So when the list is empty, how do you know if the list is empty? You could ask what's the length of the list? Is the length of the list equal to zero? That's fine. You can also write it like this, all right? Is the list equal to the empty list? That's then, yeah, it's not there. Return false, okay? How do you know when the item is there? How do you know when you found it, right? We'll go back to your search algorithm worksheet. The way that binary search knows when it has found something is whenever the middle of the list, we're still gonna chop it down where we're gonna have a first and an end. But when the middle is equal to the target, if the middle is equal to the target, then we have found it. We know it's there. So this binary search algorithm we're gonna write, it's just returning true or false is the item there. It's not returning the index or anything like that, okay? So, but now these are our what? What are the two elements of a recursive algorithm? It's progress, the recursive step that progress is stored in the base case and the base case. So we have two base cases again, okay? So a list submit, so these are our two base cases. Now, what are we gonna do otherwise? Okay, well, the logic is similar to before if we've got this middle value, okay? So why don't we let's set the mid equal to... Now, we don't have a notion of a first or last. We don't need it. We needed that for the iterative version, but we don't need it here. All we need to know is what? All we need to know is the length of our list, okay? Because we're not moving pointers. We're not keeping the whole list. We keep chopping it down and only focusing on a piece at a time with each recursive call. So all we need to know is the length of the list divided by two, integer division by two, right? Okay, so how do we ask our question? Actually, you know what? I needed to do this up here, didn't I? Yeah, I needed to do this step up there. Sorry about that. There's no resetting the video now. We're too far in. I apologize. All right, so I should have done this first. Okay, I've got my mid. Now, what am I going to do? So the binary search algorithm says if the target that we're looking for is greater than a list submit we want to do what? Well, we know that we have to look in that right hand side of the list. Okay, so how do I extract out the right hand side of the list? Well, the mid is my middle point. If I want everything to the right of the middle point, I can take the slice, right? I need a slice that looks like a list. Sub mid plus one to the end, right? So I've got just these two dots. I don't need to give a stop point here. If you do a list mid plus one, right, that starts the slice one to the right of the middle point, colon, and then a blank. If I don't specify it, it takes everything after that point, right? All the way to the end. Okay, but now I need my recursive call. So what I will return here is binary search of that. Shrink my head a little bit here. So that's my recursive call. Return this guy, okay? Else, right? Else, if target is greater than a list submit, okay, I search to the right. If target is less than, else target is less than. I've already taken care of the equal to part, right? Up here, I've already taken care of that. Else, I want to search the left side of the list. Well, how do I get the left side of the list? It's everything up to and including, or excuse me, up to but excluding the middle value. Now my calls to the binary search here, and once again, we're too far deep. The call to binary search needs to pass the target in as well, and we'll clean this up inside our code, right? Let's see an example. Let's walk through two examples, actually, okay? So let's do a binary search. Let's first search for five, okay? I've got my list X, it's got these elements in it. I want a binary search for five in that list. Is it there? I'm going to return true or false, okay? So when I first call this, right? A list is going to be one, five, 24, 32, and 100. It's this guy, right? Binary search of X, my target here is five, okay? So what does my algorithm say to do? Well, if I had drawn it correctly, the first thing I would have done, what don't I just try and fix it real fast here? First thing I'll do is mid is... That's what I get for trying to fix it real fast, isn't it? We're too deep, we're too deep. So you guys have been in a lot of live Zoom classes right now, and they're worse, right? I'm sure they're worse. I know of a couple of professors that got Zoom bombed already. Not a good time, not a good time. Okay, mid is a length of a list divided by two. All right, there we go. Okay, so I'm in my algorithm. I've called binary search with a target of five and this initial list value. So first I compute my mid, my mid in this case is going to be the length of this list is five, divide integer division by two. The middle will be two, right? Length of the list five divided by two is going to be two. All right, so to walk through the code is the list empty. No, it's not. Is a list sub mid equal to the target? Well, what is a list sub mid? Mid is two, this is index zero, one, two. Okay, so index two is value 24. Not found, not found yet. Okay, so what do I do? Well, according to my code, if the target is greater than a list sub mid, which it is not, target is five, five is not greater than 24, do this thing. So I have to search the left-hand side by calling binary search again with my target here. My target is still five, right? I have to pass this target, but it's still five. We're not going to change what we're looking for. And the list, though, has changed, right? Now my list I have taken from the first element, which is one, all the way up to but excluding, according to the slice operator, excluding the mid. The mid was two. So a list is going to be one, five, right? Okay, so this is what it looks like now. All right, let's run this. Mid is the length of a list divided by two. The length of this list is two. Two divided by two is one. Okay, if now let's walk through our pseudocode. A list is empty. A list sub mid equal to the target, you bet. A list sub one is five. Five is my target. I have found it. Here is my base case. True, return true. All right, I got an answer and that answer is true. And now you have an answer too, binary search of five on this list here. Your answer is true as well. Return up here. Our answer is true. Okay, so I invite you to search for binary search. Do a binary search of this list, but do a binary search of four. See if you can trace it out. So four is obviously not in the list, but you'll have to go through some slightly different steps before you hit the base case where you don't find it. All right, so we'll switch over to the code now and kind of wrap these examples up. Hey everybody, let's go ahead and implement our is palindrome and our binary search in the code now that we've done the worksheet and crafted some pseudocode. Okay, so I'm back here again in the Python file that I distributed to you. I have a example of how to detect a palindrome using an iterative algorithm and I'll leave it to you to kind of play with that and understand it. We can check it out real quick though. Is palindrome iterative? What's a simple palindrome? How about ABBA? Let's see if this works. True, still true. I still got some garbage up here. Let me get rid of that real fast. All right, so is palindrome seems to be working? Does it work on an empty string? This should be true or string of length one. Yep, these are palindromes. How about this? We've got a bunch of characters. That's clearly not a palindrome. So we're in good shape there. Let's implement our pseudocode, right? So we had two base cases. We had a base case where if the length of our string was zero, we would return true. And if the length of the string was one, we would also return true, right? So these were our base cases for our palindrome. Hopefully your spidey sense is tingling and you can say, well, I can actually combine these two if statements with a very simple or and kind of clean ourselves up a little bit. Now the question is how about the rest, right? These are our base cases. Now we got to make our recursive step. And what was our recursive step? Our recursive step was that what we need to do is check if the first character and the last character of the string were equal, right? So otherwise, if else, let's check is the first character s sub zero equal to the last character s sub minus one, right? If they're equal, good, true. If they're not equal, this will be false and it'll return immediately. So we're going to return this result. And remember our algorithm, if the inside of the string is also a palindrome. So if the inside of the string, we can get the inside of the string by starting at index one and going up to but excluding the stop index, which is negative one, okay? And that was it, right? So let's try our checks here. So I'll just type them back in. Print is palindrome abba. So this should be true. And how about single character? That should be true. Empty string. That should also be true. These are our base cases, these two guys here. And let's just print one that we know for sure isn't, how about abc? Boom. So true, true, true, false. Palindrome, palindrome, palindrome, not a palindrome. Looks good. All right. Now look at, let's just take a quick second to appreciate the code here. We got four lines of code, recursive solution. The iterative solution is what? One of eight lines of code, okay? Maybe I can tighten this up, maybe not. Four line solution, eight line solution. Pretty clean, right? So we strive for precision and clarity when we're writing our code. I like this, I like this solution. There's some down tides to a recursive solution, which we'll mention here in a minute, is just they can get expensive, right, in terms of memory. Lot more so than iterative methods, because every time a recursive method calls itself, it's copying some data, it's creating some new variables. It's got to create that stack entry for the recursive call. So they can start to eat up memory, but for this nice little solution, it looks good. Let's go down to binary search here. Here's my binary search algorithm from your worksheet, the iterative version. It works, trust me, it works. It's not too bad to understand, but let's go ahead and implement the recursive version, right? So what was the recursive version? Well, the first thing we needed to do was we needed to compute the middle. And we said that the middle is the length of the list divided by two, okay, integer division, right? Now, what are our base cases? Our base cases are, why are you so mad at me? Got an extra equal sign, there we go. All right, so what are our base cases? Usually, again, the base case is going to go at the beginning of your algorithm. It's going to go at the beginning of your code because you want to stop as soon as you encounter one. So our base cases were, if the list was empty, all right? If the list is empty, bay list equal equals this. And again, you could also do if length of a list equal equals zero, that's fine. Why don't we do it that way? If the list is empty, you know that the thing isn't there. You kept chopping this list down until there was nothing else to look at. And it wasn't there yet, so it's not there. Return false. Else, if the target is equal to the item in the middle, you have found it, right? That is the definition of binary search. That's how this guy works up here. If the target ain't less than, if it ain't greater than, it must be equal to. That's what you're looking at, the middle of the list, right? Same thing here. If the target is equal to the middle of the list, return true. What else? Well, if the target is less than, let's do greater than, if it's greater than the item in the middle, well, we want a binary search, but where do we want a binary search? We want a binary search the right-hand side of that list, okay? So we're going to return the search result of searching that right-hand side of the list. We've got to return here, right? If we don't return, we'll never get an answer. It'll be an infinite loop, basically. So we've got to return out of our functions. That's something you always got to do in Recursive Function. You've got to return. If you don't return, if it just keeps calling itself and calling itself and calling itself without ever hitting a base case, infinite loop, okay? So we're going to search the right-hand side. We're still looking for the target. We're making this call right up here, but we're chopping down our list and we want to search the right-hand side. So we want to search everything from the middle of the list, plus one, so one step to the right, all the way to the end. So I leave this blank here. I could do, I could put something in here, but if you just leave it blank, that's going to take everything from the start to the end. Otherwise, the target must be less than the middle. So I need to search the left-hand side of the list. How do I get the left-hand side? Well, I take everything from the beginning up to but not including the middle. So by leaving this blank in here, that says start at the beginning, start at index zero. You could type this. That's totally fine if that's helpful. All the way up to the mid, but excluding the mid. Remember, in the slicing operator, you go up to but excluding the stopping point. Let's convince ourselves that this works. Let me make a list real fast. 32, 66, 78. Now, what property does a list have to be if you're going to binary search it? Well, the list has to be sorted. So it's important that these things are sorted, otherwise binary search doesn't work. But let's try it. Let's binary search for something that's in the list 24. It should be there. Let me comment these guys out so we don't get any extra spam. So print binary search 24. What do we get? Oh, I didn't pass the list. Am I listening this X? Okay, binary search of 24. True. Hey, I found it. Binary search. Let's search for the thing at the end of the list. It's good practice to test things that are like at the beginning and the end. I should be able to find 100. And how about something that's not in there? How about five? Five ain't in this list. So let's search. So I should have true, true, false, and voila, we do. So look at this. It's a series of if-else statements, but I don't know. Maybe I would argue, especially once you get a little familiar with recursive calls and a little less scared of them. This is a little more clear than this. It's also a little shorter, too. So there's good reason to use binary search. All right, let's just kind of wrap up really quickly here. Remember, remember, remember, remember. Get through this. There are two things that every recursive function has to have. It has to have a base case, which the return value of the function is specified for one or more values of its parameters. You've got to have one or more base cases. You also have to have a recursive step. So the current value of the function is defined in terms previously defined function values and or parameter values. This recursive step must make progress toward the base case. Okay, so these are the essential elements of a recursive algorithm. Most recursive algorithms have the structure where you say if one of your base cases, if some stopping condition is true, you've hit a base case, else do the recursive step. So, and remember, there may be more than one base case. All right, we had our examples. There's some cool things you can do with recursion, some visual things. Check out chapter 5.8 of your book. You don't have to implement the code, but they show you how to draw a Sierpinski triangle, which is a recursive structure using the turtle. And then fractals. Fractals have a very natural recursive definition. There's a link there to some cool fractal images that are created in Python. You can try out it yourself. All right, that's it for recursion. We will see it again when we talk about sorting after the holiday. And whenever we get into our final data structure of the semester, which will be trees and binary search trees. Till then, have a good weekend. Stay safe.