 All righty, welcome back to 105. So yeah, I guess a lot of people distracted by the eclipse that you can't see here, because it's too cloudy. So yay. All right, so I think, pretty much last topic of the course, it is searching. So when you have a lot of data, well, you probably need to search through it at some point. So previously we searched through like nodes in a link list. Today we will focus on searching in arrays. So today our entire task is to find the index of a value in an array and then we just return negative one if it is not found. So we probably already know how to do this. The first searching algorithm, if you can even generously call it that, is called linear search. And all it does is iterate through the array, looking for the value. If it matches, it returns the current index. So this should be child's play if it's on an exam or something like that with how to code it. So how would we code it? Well, we need to search through the entire array. So we would have a for loop because we know how many elements we have to go through. So I could just do int i equals zero all the way up to i is less than array length plus plus i. And then I can just simply check that, hey, if the value in the array, so at array at index i is equal to the value I'm currently searching for. So this is the value I'm currently searching for. Then I can just go ahead and return i because while that's the index that matches it, otherwise I can return negative one. So anyone disagree with that? Looks like a perfectly Cromulent search, perfectly good, perfectly something we could write. So if I go ahead and I have this giant array and I'm trying to search for the value six, well, in this array it's at index, well, arrays start at index zero. So it would be at, this is zero, one, two, three, four, five and six. So value six is at index six. So if I go ahead and run this, whoops, compile, whoops, I'm in the wrong directory. So if I go ahead, compile and run it, it should say it's at index six. If I try searching for, I don't know, 10, should say it's at index zero because well, it's the first element there. Right, so founded at index zero, joy. Any questions about that? That doesn't seem terribly exciting, right? All right, so let's add the excitement for today if you can generously call it that. So we saw sorting before, so what if we actually search the sort data? And for instance, if the values are sorted, how could we find the minimum or the maximum value if everything is sorted? Yeah, so if it's sorted in, let's say, ascending order where the lowest is first and then we have the highest later, well, the minimum value is whatever's at the beginning and the maximum is whatever's at the end. So it's really easy. If it's in descending order, you just flip those. So can we think about ways to sort data better if it was sorted? And everyone is probably too young to remember phone books. Anyone have a phone book? One? So if you need to search someone's name in the phone book, what did you do? Yeah, yeah, so it's in alphabetical order. So if you wanted to find someone's whose last name was, I don't know, E for instance. Well, it has like black squares in alphabetical order because you're a human. You know, E's like kind of near the beginning. So you might like kind of search, find the E tab and then, well, I guess if you're finding my last name, you just go to the back because it's EY. So that's probably near the back. But if there are a bunch of EYs, then what would you do? Probably like flip to a random page, right? And be like, oh, well, flip to a random page, pick a name and then see if it comes before or after my name. And then, well, if it comes before my name, I search after and similarly if we reverse it, right? And in general, I can generalize this a little bit and if I didn't, like, we kind of know where letters are supposed to be alphabetically, but if we didn't have any intuition about that or how many things or there wasn't the tabs or anything, as long as our data is sorted, well, we can cut our search space in half each time. So if we have a giant array, we can check the middle value and then compare it to what we're searching for and same idea. So if the middle value is greater than what we're searching for, that means the value I'm looking for is in the left half of the array. So I don't even have to bother searching in the right half of the array. I can cut off that entire half. And then similarly, if that middle value is less, then I just have to search the right half. There's no possibility that that value is in the left half. So I could do this, I could just keep on doing that. So I could split it in half and then, while in that half, pick the midpoint again, then do the same thing, split it in half, split it in half, split it in half, until I either find the name or I'm just looking at a single element or my array bounds are not valid. So that is something called a binary search and something that we can go ahead and implement. So we have lots of times, I'll take suggestions of how we would implement a binary search. So I started off, while we know we kind of like need bounds what we're looking for. So maybe our first bound is the entire array. So I create a variable called low, that's equal to zero because arrays will always start at zero. And then a variable called high that will start at array length minus one because that is the last index of our array. So let's try and develop this together since we have lots of time. So what should my first step be? Yeah, so I can think about what happens initially. So val is the thing I'm searching for and I can compare it to what currently is the middle of the array, right? So how would I calculate the middle of the array? So let's say I create a variable called mid, how would I calculate what the middle index of the array is? Yeah, sure. So would you say array? So something like that, high minus low. So high minus low divided by two, yeah. Oh, okay, so this divided by two. But you're arguing that is high plus low, sorry? Oh, that whole thing minus low? Just what I have here, this is good. All right, so let's think, is this good? All right, so if you want to see if it's good, good to pick some random numbers, yeah. Sorry, I couldn't hear you. Yeah, so we have a dissenting opinion back there that he says high plus low. So let's see here, let's take an example. So what if, I don't know, low? So let's say we have an array that looks like, I don't know, we have five elements. So we have one, here, I'll just call them by their indexes. So we have five elements that looks like that, right? If I wanted to calculate the mid of, let's say, this two and this four, okay, stop. Then the mid should be three, right? So let's just put in the numbers. So high minus low, well, that would be two. Two divided by two is one, so that would give me this, right? So that doesn't work. Maybe I could do it where I just add low to it. Yeah, that seems like it kind of works, but yeah, that turns out to be the same thing as just high plus low divided by two, right? And then that gives me the middle index. So we like that middle index? Yeah, I can't hear, sorry. Yeah, so now I have to start comparing the value. So I can compare the value to array mid, and then one thing is, well, if it's equal, that means I'm done, right? I found the value, it's at the index provided by mid, so I could return mid. All right, so if it's not equal, and I'll take someone else. So give someone else a chance. Someone new, what should I do here? Yeah, yeah. Yeah, so check it is, so the current mid value is, let's go with, I don't know, greater than. So we can try and think about this. So let's say the value I'm looking for is, I don't know, say it's just some big number. So say it's like 10 or something like that. So I'm looking for 10, right now, 10 is greater than two. So that means I need to adjust it. I need to look over essentially to the right. So I would keep my high the same in general, and then adjust my low. So my low should now not be zero. My low also probably shouldn't just be two, because I already checked two, so I don't need to check it again. So it should probably be equal to mid plus one, right? And then my other one is, well, if it's the other direction, then I need to search this way in my array, and I already check this value. So I should leave my low alone. That won't change. And then adjust my high to be equal to what? Yeah, mid minus one, right? So I want to go over one to the left. All right, so that looks good. That's like one iteration of the loop, right? So I should probably put, like run this however many times I need to, right? So do I know how many times this is going to need to loop for? No, right? So probably need a while loop. So I'll just put while. Yeah, let's just give it a while one. All right, how do I know when to stop? When low equals high. So when low, like that, or low does not equal high, right? Don't know, just guessing. So if we find mid, we'll return and we don't need to break as the function stops, right? So we have a return here if we actually find it. So just low, not equal to high. All right, let's try it. So here's our big array we're going to search for. I just sorted it. I'll print out the sorted and then we can call binary search. We're looking for a six. So this is our current code that I call this binary search. So found it at index four, seems to be right. What about if we're searching for something that is not in it? So what about if we're searching for five? So it should return negative one in this case, right? So here, yeah, just returns here. I need to print out something. So not found seems like it works. What if I search for, I don't know, seven. Not found, search for, okay, seems to work. Although there is kind of a corner case here, but I guess it didn't come up where technically low could be less than mid. If we're like searching at one element, it doesn't match. And then we go backwards run. Like if mid and low were the same value for some reason. So if they were both the same value, then we would check if it doesn't match, then low would become one and high would become zero. Like it would just stay at zero. So we'd have some weird bounds. So we should probably just go through this while I would argue while low is less than or equal to high. And that will probably get rid of that corner case, even though I couldn't actually do something with that. So, did do, we can search for eight. All right, cool. So, any questions about the code? Here, let's see if I can fit it. So, any questions about that code as we have written it? Yeah, so yeah, why I put this, so if low is not equal to high, like if this is false? Oh, like why I changed it? Okay, so before it was, let's see if I can make it break. So here, so we have an array only has one element in it, and we're looking for value one, right? So if we do this, okay. All right, well, let's see why that worked. All right, so in this case, low is equal to high. So I think, I think if we search for two, it says two's not there, right? So because I have not equal, so my, let's consider the case, the array only has one element in it, right? So low is equal to zero and high is equal to zero. So low is equal to high, so it would just skip all this and it wouldn't check, right? So it wouldn't check and that just says it's not found. So really, like if low is equal to high, I still want to check. So if I do this, then I know I have bounds or they're the same. And then if they're the same, like mid is just going to be that same index, right? I'm just gonna add the number twice or multiply it by two, then divide by two. So I get the same number. So in this case, where high and low are both zero, zero plus zero divide two, so it's still zero. And then we actually check the value and then see whether or not it is the value we're looking for. If it's not, when we adjust it, low will be equal to one and then high is equal to zero still. So we clearly shouldn't check it because like low should always be less than high. And then similarly, if it was the other way, high would be equal to negative one. So shouldn't check that either. So we should just stop that. So yeah, we definitely need low, less than or equal to high because we want to make sure we always check. All right, any other questions about this fun binary search? So here, let's make it even more fun because if doing it once is great, can we write it a second way? So if we look at it like when we were talking about it, it's like if the array is sorted, when you check the middle value, if it's greater, just search the left half. If it's less, search the right half. Sounds recursive in a way, right? So if we wrote this recursively, what would our base case be? So I'll give you a second to think about that while I delete stuff. I guess the eclipses over two, could you see anything? It looked too cloudy. You could? Not really, it just got a little dimmer. Nice. Yeah, sure went to Niagara Falls, I guess. Yeah, unfortunate that we are here, but I guess somewhat fortunate the weather's so bad it doesn't matter. Yay. All right, what are our base cases here? If low equals high, careful. So if low equals high, we still want to check that element, right? Unless you're talking about it, putting it after we check, which I guess we could do. Yeah, we could do that, okay. So if we look back, let's go here. So if we want to write that recursively, our base cases, well, if we want to just copy what we had before, our base cases are, if we want to put the check first, if low is greater than high, that's one of our base cases, right? That means we're out of bounds, it doesn't make sense. We want low to be less or greater than, sorry. Low should be less than or equal to high. So this could be one of our base cases where we just return, we have not found anything. Otherwise, we could do the same thing where we calculate the mid index. So that's low plus high, divide two. And then our other base case where if we check array mid, and our value matches that, so we could return mid and then, and what would happen if I did this? Sorry. Yeah, well, if I do that, that's an assignment statement. Bad things will probably happen, will probably be true unless I'm searching for zero, which case, yeah, in which case bad things will happen. So always remember, that's a common issue on exams for getting to use equal for comparison. So those are our base cases, and now we could do our recursive cases. So for our recursive cases, what are our recursive cases? So that's the whole thing again, yeah. So if the value at mid, so if the val is greater than the array at mid, then what should I do? So we can come up, so I never remember this either, just write an example. So if we are searching for, I don't know, we have one, two, three, and we're searching for some big numbers, say we're searching for, oops, searching for 10, something like that. So val is 10. So right now, 10 is greater than it than two, right? So I need to search to the right. So to the right, if I wanna be recursive, I could call recursive binary search helper with the array. And then here, if I'm searching to the right, I want to adjust low, right? So I want low to be equal to mid plus one, keep high the same, and keep the value I'm searching for the same. And then in the else case, what should I write? So it should be recursive binary search helper. Here, I'll just put this. All right, that work? No, uh-oh, what did I miss? So you said change high? Yeah, so change high to mid minus one because I'm searching to the left. I'm doing the left half, right? So we go ahead, we compile that, sort the array. We're searching for six again, which should be at index six, I believe, or sorry, four. So found six at index four, which we can check. So we got zero, one, two, three, four, so seems to work. So two different ways to write it. If we write it recursively, right? We needed a helper because we need to keep track of high and low. So that's why we wrote the helper function that took high and low as arguments. And then we go ahead and we adjusted them in our recursive calls. Otherwise, we just kind of treated the loop bound as one of the base cases and the case where we actually find the value as the other base case. All right, any questions about that? Are we all having fun with searching? All right, I'll leave you with, or I'll have you think about this and then we can take it up. So harder thing to do. So if I go ahead and let's say I search for an eight. Oops, not 82, eight. Actually here, I'll change that to a six for it. So if I search for a six, I'll make it a bit bigger. It finds it at index four, right? So zero, one, two, three, four. All right, all right. So what about if I search for eight? What index is it gonna give me? Six, six, we got six, six, six. Whoops, shouldn't have said that, whoops. All right, so we're guessing six. If we compile it, but binary search, whoops, searching for eight. We got six where we really guaranteed to get six, maybe not, let's go ahead and add another eight. So now we get seven. We're seeming to always get the last one. What about if we have, I don't know, a lot of eights? So now we get index nine. So what is that? So zero, one, two, three, four, five, six, seven, eight, nine, 10, 11. So we got like an index like somewhat in the middle, right? Maybe I don't want that. So how would I change my code that searches for the number two? Let's say always get the first match without doing linear search, with doing binary search. So I should be able to change something in my binary search code, whoops, such that I always get the first match. And I'll leave you with this for a few minutes to think about it, because it would be a good exam question if I was given the task to come up with exam questions, which I might be asked to do. This would probably be fairly relevant. So I want to write, and again, just to be clear, I want to rewrite this binary search, such that I get the index of the first match in the case there are multiple matches. So in this case, I always want it to return zero, one, two, three, four, five. So I should get index five if I'm searching for an eight here, one second, I'll give them another minute. Give you another minute to think about it. And again, something good to put on an exam. Okay, so we got our first one here. So if it matches, you want to check the whole array. So that would, I would consider that linear search if you're checking the rest of the array. Because you want to, I see where you're getting at. So you're saying as soon as you find the first match, just like start iterating to the left and going over one by one. So that would technically be linear search. We can do a bit better. Yeah, so if it's a match, then I still look to the left. So if it matches, I search the left. Okay, so how would I change that? So I have a match here. So you just want me to search to the left instead. So instead of returning mid, you want me to search to the left. So you want me to do this instead. What else should I do? I should update what? Sorry. All right, we need to think. All right, yeah, without linear searches. So you're asking if I can check the value to the left. So yeah, your idea is if I find it, I check the value to the left of it. Yeah, sure we can. So here, I found the value. So what is the value to the left of it? So you're saying that here, if array mid minus one is also that value, here, sorry, I'll change it around. So if the value I'm looking for is also mid minus one, then I want to look to the left, right? So this is not my recursive one. This is just the normal search. Yeah, this doesn't have the recursive one. This is like the iterative one. So this one is not the recursive one. This is the one with the big loop. Well, we should be able to do it though, but so I'm looking at high to the left and then that's it, right? Yeah, cool. So this actually seems pretty good. Your idea was like, hey, we need to keep track of mid if we need to return it, but if the value to my immediate left is not the value, then that means that this is the first match, right? If we're here, so then I know that I am the first match, so I don't need to save the value or anything. I could just return mid, something like that, right? Does that seem okay? All right, let's go ahead and try it. It seems to work, right? Yeah, yeah, yeah. So the comment just was that, oh, well, let's say I was searching for, I don't know, what's the first one? So let's say I was searching for a one. What happens when I search for a one? Well, it works. Should it have worked? Probably not, because if we were searching for one, well, mid was equal to zero because, well, it's index zero. And then we went to mid minus one. So what's zero minus one? Minus one, is that a valid index to use in our array? No, so we did something bad. If we ran valgrind, maybe it helps us, maybe it doesn't. Oh, it gives me some errors for other reasons. All right, so turns out I shouldn't have done that. So I should have also checked that if, in this case, mid is greater than zero, right? So I need mid is greater than zero and this to be true. So, whoops. So that should work, right? So, here, one sec here. To do that. Oops. All right, so go back. All right, so that, should that be the condition or should it be an or? Any thoughts? And, yeah, should be an and. So, oh, here, so that was search for one worked, search for eight. So, yeah, could be an, should be an and, wait, where's our code? Yeah, so, if mid is greater than, or if mid is equal to zero, then this is false. Whole thing is false. Turns out shouldn't really matter if we change it to an or. I think it should still work if we change this one to or, right? Oh, yeah, so if we do change this to or, yeah, then that wouldn't work because mid, well, it's probably always greater than zero. That would be true. And then, yeah, that would screw up. So it does have to be like that. So it does have to be an and. All right, perfect. Sweet. All right, any other questions for this code? All right, what about the other one? What about if we wanted to do the last match? Yeah, basically the same idea just reversed, right? So instead of mid having to be greater than zero, like it can't be equal to zero, I need to make sure mid is not the last element, which is a ray length minus one. And then I check to make sure the thing on my right is greater, right? So that would be mid plus one. So, and then all I do is I need to, if it is, then I have to search to the left instead of the right. And it should still work. So if I go ahead and combine these now, so now they're guaranteed to get the first index and the last index, then we can go ahead and we combine them together to write a more useful program. Oops, this is not the most. Yeah, so we could have rewrite binary search so that we call binary search first to get the first match. And then if there is no match, so it returns negative one, we just say it's not in the array. And then we can actually calculate the total number of matches. So we could get the index of the last match and then after we get the index of the last match, well then last minus first is plus one is the number of matches. So we can just output exactly the number of matches and we're using a binary search for both ways. No linear searches at all because linear searches are slow because that's like ON. Well, if you can split the array in half every time, that's actually like log N number of steps you have to do, which is a lot faster. So this one will work. There's no linear searches. There's no anything at all. And if I go ahead and run it, hopefully it works. Now I have eight and then it says, wow, I found two matches, joy. Yeah. Yep. Yeah. So in here, let's say, so your question is if the array was full of the same value, one, all right, full of the same value. So I got that, whoops, eight is definitely not in there. So we'll search in for a one. Oh yeah, there's a bug in my code. Whoops, found zero matches because in my code, I screwed up and I need to update the slides because that's why I asked about the and because I accidentally wrote it. So I just rewrote it a little bit to combine the cases there. Shouldn't make a difference. Wow, my code is screwed up. All right, I failed the exam. All right, I will fix that after. So yeah, so the question, if this was to work, does this count as linear search? So that would not count as linear search because like linear search or binary search first would start at the midpoint, then go like here, then go here, and then eventually it would make it to the beginning after cutting it in half a whole bunch of times. And then the last one would go start here, cut it in half, cut it in half, cut it in half and then cut it in half until it gets to the end. And then I'm just doing like I'm repeating the same cutting in half two times, but the constant factor doesn't really matter. Yeah, so if this actually worked where it found the first match and then the last match, the first match should be at index zero, right? And the other one would be at a rate length minus one. So that should just say everything match. Yeah, it should count the whole array. So it should say the entire array not not found because I screwed up my code. And I will fix that and post it on the slides and not do whatever I did. But yeah, live testing, isn't that great? All right, so the rest are on the slides that I have to fix in summary for today then. So if the array is sorted, we can search it faster. If it's unsorted, well, the best we can do is a linear search, which is just iterating through the array, checking the values, seeing if it matches and returning the index if it doesn't. However, if the array is sorted, then we can use binary search, which is just the idea of cutting the array, like just cutting the array in half every time until eventually, well, until you find the element or you are outside, like your bounds don't make sense anymore. And then, well, that's obviously not in the array. So you can return it is not found. So with that, I will be around. So just remember, phone for you, we're all in this together.