 Hello everyone. So this video we will talk about removing an item from the list. So removing, if you recall from, you know, regular Python lists, you search for a value and then you take out the value, the first one, the first occasion of that value that you find. And that's what it does, that's it. In linked lists it's a bit of a bear to implement. So grab your caffeine or whatever you need, because this one will be a little more involved, a little more challenging. Alright, let me switch over to my whiteboard. So I want to talk through some scenarios first, then we'll derive an algorithm, then we'll kind of maybe code just a little bit, okay? So focus, look at this part of the board down here, big part, I'm just going to leave this part alone up here, we're going to work down here. And again, I just want to consider some scenarios first. Alright, let's suppose that we want to remove here the value 42. Okay, so 42 is in our list, we know that, it's right here. Okay, now just conceptually speaking, looking at the list, forget about the code, how are we going to, how do you remove 42? Well, the way you would remove 42 is first you got to make sure it's there. Okay, and then what we want to accomplish is we want to connect Bob to Jane. Okay, that's how we're going to cut 42 out of this list. Because if Bob forgets, right, if Bob forgets where 42 is, then 42 is effectively gone, right? There's no way to get back to this node 42, because nobody remembers that he's there. And in fact, that's what Python will do. It will say, man, you're not stored in a variable anywhere, Mr. node 42. So I'm just going to take your memory back. It's called garbage collection. It's the technical term for it. So if no variables like Bob's next, no longer point to this node, the node goes away. Don't need it anymore. Nobody knows it's there. So how can you use it? Okay, so that's how removing from a linked list works. You just, you don't like have to call the delete keyword or anything like that. You just say, All right, all variables that point to you, I'm going to have them looking at something else. Okay, so that's what we will do. We're going to tell we need to tell Bob if we're trying to remove 42, we got to find 42 and we got to tell Bob point to Jane instead. And that effectively will wipe 42 out of this list. Okay, all right. So let's get back to our original state here. How are we going to do that? What's our strategy going to be here? Well, before when we talked about search, search is kind of a part of removing to because we got to when you remove something, you remove it by the value. So first kind of like one of the first things you have to do is you have to make sure you have to try and find the thing. Okay, in the way that we found the thing in the linked list was we had this variable called Kerr and we kind of walked it down the list, we traversed the list to find the node containing the value. Right, so we're going to do something similar again. Okay, we want to find the value or the node containing the value we want to remove. But just knowing that node is not going to be quite enough because we got to tie the previous node here to this one. Okay, so in the general case what we're going to need is actually two variables. Previous, one called Preve, which is for previous, and one called Kerr, which is the current node we're looking at. And these two are going to walk in tandem down the list. Right, and if we know the node before the one we wanted to leave, that's how we can make the link from the node before the one we wanted to leave to the one after the one we wanted to leave. And the line of code that's going to do that is previous.next. Right, because there's a previous here. .next. How do I assign this .next to Jane? Well, who knows Jane? Current .next knows Jane. This is the snip snip line that's going to get us kind of cut out of here. Right, that's the line of code that's going to snip the chain. It's going to break the chain for us. Okay, so we'll kind of talk through the algorithm in a minute. There's going to be other scenarios, though. Right, we've got at least three scenarios we've got to handle. Probably four, I guess. Right, one where the list is empty. Okay, so empty list. One where we're deleting from an empty list. Can't do that. Right, two, where the thing we're removing is the head. Okay, we have to deal with this specially. Okay, why? Well, if what we're trying to do is delete Bob. If what we're trying to do is delete Bob, and our key term here is we've got a previous and a next. Well, what is going to be the previous? What comes before Bob? If we're searching for this thing, current will point to the thing we're looking for, which is Bob. But what comes before Bob in the linked list? Nothing. Right, Bob is the head of the list, so previous will actually be none in this case. So we got to handle that. All right, we got to handle the case where we're deleting Bob. The other case that we got to handle is where we're deleting the tail. The actual deletion step doesn't look any different, but we got to update our tail variable. And then the fourth case is where we're deleting from the middle, and that's the one we just went through. So there's a lot of stuff we got to contend with here. But in general, right, we're going to have kind of a two step process here. In general, we're going to have a two step process. The first step is going to be try and find the thing. Okay, one, find the value you want to remove. Okay, we're going to, and as part of that, we're going to walk, curve, and preef down the list. Okay, I'll show you some code here in a second. And then the second thing we need to do, remove the value from the list. Okay, we also need to decrement the length of the list as well. Remove value and decrement the length. That's if the thing is there. What if the thing is not there? Okay, well, if you go to the specification for remove in your assignment, or if you use remove in a Python list, or we are going to raise a value error if the item is not in the list. Okay, that's what the specifications say to do. All right, so this is tricky, right? There's a lot we have to deal with here. Okay, so let's try and at least mark up our code a little bit to get us started on this. Switch it over. All right. All right, so here's our specification for the remove method. Remove removes the first item from the list first only. Okay, if there's multiple items with the same value, if I've got multiple bobs in my list, I'm only taking out the first one. Remove the first item from the list whose value is equal to the item. The method does not return anything. And the method must raise a value error if there is no such item in the list. Okay, so what going on? We can handle our special case first. Why don't we do that? So let's let me type down our algorithm. So let's handle our special case first. Second, next part of our algorithm is searching for the item to remove. Third thing, remove it and decrement the length or raise a value error if not in the list. Okay, so three-part program here. Three parts of our kind of computation step. All right, so let's do it. Let's handle our special case. Okay, so what's our special case? Our special case is if the list is empty. How do we know if the list is empty? Well, I like to do if the head is none, the list is empty, right? The list must have a head. If it doesn't have a head, something's gone horribly wrong because you can't find anything in the list. Now if there's no such item in the list, well an empty list has no items. I say raise a value error. And we talked very briefly about how to do this on the previous videos. You use the keyword raise, value error. That's it. Okay, so if you're trying to remove from an empty list, no, can't do that about the thing ain't there. All right, part two. Let's search for the item to remove. All right, I'll even take these comments and move them down here. Okay, search for the item to remove. Okay, well searching is gonna look kind of like this, right? Kind of like we did up here where we walked down the list, except as I mentioned on the board, it's not enough that we just know the current variable, right? We start by looking at the first thing, the head of the list. Current is gonna walk down the list. It's gonna be a variable that walks the list. But it's not enough that we know just the node that contains it. We got to know the node who came before, so we can snip it out of there. Okay, so if we start at the head, remember, I'm trying to scoot this over, if current starts here at the head of the list, who comes before the current, the head of the list? Nobody, right? There's nothing before the head of the list. Okay, so we're gonna start by saying previous. Previous is gonna be the pointing to the node before the current one we're inspecting. We start out by saying none. There's nothing before the head of the list. Okay, so now let's walk. Let's walk our list. Okay, and in fact, let's, you know, let's take this. Okay, here's our walking code from the search function, or the search method that we wrote. We can take this and reuse it, right? Because that's what we're doing. We are searching. Okay, while curr is not equal to none. Okay, walk down the list. If curr.data equal equals the item. Okay, who? If it is equal to the item, we want to stop. We're not going to return true. That's something you did for searching. What we're trying to do here is find the item in the list, and we want to stop at this point. Okay, so what I can do, I actually don't need to do anything. Okay, because if it's equal to item, what you, I guess if you really want to have control and mirror the structure is, you would break right here. Okay, what does a break statement do? The break breaks out of the innermost loop, right? So this while loop, this says stop. Don't go any farther. Okay, I don't want you to loop anymore. This would stop the loop when curr.data is pointed to the thing you were looking for. Okay, that's what you want. Now, what if the current item's data is not the thing you're looking for? Well, we got to move these two variables. Okay, remember we're walking two variables now. We're walking previous, which points to the value before the current node, and current. So we got to move up the previous. Okay, got to move up the previous, and then we do current gets current dot next. Okay, previous gets curr gets current dot next. Okay, let me illustrate what's happening there on the board a little bit. Okay, let's board over. All right, so again, we're walking the list. I'll make the list a little bit bigger. Actually, I don't think I care too much about what the value is. I just want to illustrate how we're walking here, right? Let's say that we're trying to remove the string C out of our list, and let's say that our data is ABCD strings in here. Okay, so this is my linked list this time around. When we start, what does this walk look like? Now, again, we're walking down the list while curr is not equal to none. Current gets dot data equals equal to break. Otherwise, here's the part I want to focus on. Previous gets curr, curr gets curr dot next. So when we start, when we started our loop, curr is here, and previous is none, right? He's not pointing to anything because nothing comes before the head. This is the tail, okay? All right, so is this data item the thing we're looking for? No, it's not, okay? So what we said is previous gets curr, okay? So previous gets curr, previous is going to point to the address, point to the node that curr currently points to. Curr gets curr dot next. Well, curr was right here. Now, what's curr dot next? This, right? This link right here. Curr gets curr dot next. We'll move curr down here. So now we've got current and previous. We loop around again. Is curr dot data equal to the thing we are looking for? Nope, it's not, okay? Curr previous gets curr, previous gets curr, then curr gets curr dot next, okay? So we're walking these two variables kind of in tandem down the list. Why don't I physically move them over just a little bit? Virtually physically walking them down the list, okay? Loop comes around again. And again, take a look at the loop. While curr is not equal to none, curr is not equal to none, right? This is only going to happen if you walk off the end of the list, so to speak, if you go past the tail, okay? Alright? If curr dot data equal equals item, break, break out of the loop, okay? Is curr dot data equal to the item I'm trying to remove? Yeah, it is. So I stop. The loop stops. And when the loop stops, it breaks out, stops executing, these two are still there and they're pointed to the places we need them to be pointed to, okay? Pointed to the places we need them to be pointed to. Alright, so let's get back into our code. Now we're ready to like maybe snip, snip, snip, alright? So let's get back over here into our code, okay? So this batch of code is searching for the stuff to remove. Now we need to remove it and decrement the length or raise a value error, okay? Okay, but removing it is maybe not so straightforward, right? Because we got to think about, oh man, am I removing the head? Am I removing the tail? Removing something in the middle? What am I doing? Or raise a value error if it's not in the list. This is a complicated algorithm. I'm not gonna lie. It's probably the most complicated algorithm you've written in your life and maybe will throughout the remainder of this semester, okay? It's alright. It's hard trying to walk you through it because I want you to understand it as well. Alright. And would I expect you to think of all these things? No, I would say no, not necessarily. Because if you would run into all sorts of problems or weird errors and failures if we don't think of these things. So would you find them? Yes, you would find them. It would be painful figuring out, man, why is my list not working? I'm trying to delete the last thing in the list. Why isn't it working right? You'll probably still get some of that. But, you know, I have experience with this and we've thawed it out, right? I've thought about our special cases and a lot of times if you just, if you can think about those special cases, you can avoid a lot of these errors, these exceptions. That's what exceptions are. They are exceptions to the execution. Things that Python doesn't know how to do or things that your code doesn't know how to do. So try and think of the exceptional cases. Alright. Speaking of which, let's deal with this first. Let's raise a value error if it's not in the list. Okay. Well, how do you know that the thing you're looking for is not in the list? Well, just like with searching, if I get here, the thing is not in the list because the loop, well, if I get here, the thing is not in the list. Well, that may not be the case, right? Because I break out of the loop. However, how can I be sure I'm at the end of the list if I didn't find it? Well, cur will be equal to none, right? Because cur gets cur.next. If I get to the tail, the tails.next is none. So if cur is equal to none, I know I've walked off the end of the list without finding the thing. Okay. So if cur is none, I'm going to raise my value error. Right. That's what I wanted you to do. You know, raise a value error if there is no such item in the list right here. Okay. So that's what I'm going to do. Cur is none, raise it. Okay. All right. Else. Let's think about this. Else, if I'm removing the head. All right. If I'm removing the head, how do I know that I'm removing the head? Well, if I'm removing the head, this guy, previous, will be none. Right. Convince yourself of that. We start out by setting cur equal to self.head and preview equal to none. Well, if this breaks after one, after this line, this loop breaks right here the first time through, cur is self.head, previous is still none. So if previous is none, I'm removing the head. So how do we remove the head? Well, so that's actually pretty easy. Who knows the head of the list? Well, self.head is the head of the list. Right. This guy is pointing to the first item in the list. So if I need to chop off the head, I say, well, you know what? Why don't you go ahead and point to the guy that comes after you, the second item in the list. If I'm removing you, the new head of the list needs to become the second guy. Okay. How can I do that? Well, I say that the head of the list becomes the guy after the current head of the list, after the next. Right. So I do self.head get self.head.next. Or you can do cur.next. They're going to be the same thing. Right. Actually, let's do cur. Right. Because cur is pointing to the thing I want to remove. Right. Self.head gets cur.next. Done. That tells the head point to the second guy. Okay. That's it. All right. I've also just removed a node. So I need to decrement the size. Now you're going to love it. You have to, there is a special case here. The special case here is what if there's only one node in the list? Okay. Well, if there's only one node in the list, then what? Well, now the list is empty. Okay. Now the list is empty. Let me switch over. I'm telling you that, like I said, this is the most complicated algorithm you've encountered. The list is empty. Or it only has one item. We're removing a from this list right here. If there's only one item in the list, the head and the tail point to the same thing. Now, whenever we do that, right, cur for in our remove algorithm, cur will also point to this and previous will be none. So if I remove the only item in the list, okay, I need to set the head, the head needs to be none, and the tail needs to be none. That's what indicates an empty list, right? There's no items in it. The head doesn't have any data. Okay. Okay. So we got to make it so that the head and the tail are none, right? If there's one item in the list. Well, how do I know that there's only one item? Well, you can look at the size, right? Maybe that's the most sensible. You could also say if the tail and the head point to the same place. If the tail and head point to the same place, then you only have one item in that list. Or you can ask yourself, is the size of the list one? Which one do you want to do? Why don't we do if the size of the list is one? Okay. If self dot size equal equals one, right? What do we need to do? Well, let's set the tail equal to none, right? We're going to empty out the list. And actually that's sufficient, right? Because self dot head gets cur dot next. If there's only one item in the list, what's cur dot next, right? If there's only one item in the list, cur dot next is already none, okay? So since cur dot next is none, self dot head gets cur dot next will zero out, okay? So this is good. This will do it, right? Craziness, right? Let's think about the middle of the list next. I need to remove, oh, did I go to the wrong place? No, I'm okay. All right, so let's think about removing the tail or the middle of the list now, okay? How do I remove the middle of the list? Actually, you know what? Let's focus on removing the tail. How do I know that I'm removing the tail of the list? Well, if the current node is equal to self dot tail, I'm removing the tail of the list, okay? How do we remove the tail of the list? So the tail of the list is going to be that, well, let's just draw it real quick. We'll just put, I'll put a second item in here. So in this case, the tail is B, the head is A, and we want to remove B, okay? So following our algorithm thus far, the algorithm will be that current is pointing to B, right? Previous is going to be pointing to the head, okay? So if we're removing the tail, how do we chop off the tail? Well, all we need to do is tell this guy, Previous, to forget about it, the next person, okay? So Previous, forget about the tail, just cut it out of there. You don't need it, okay? So how do I do that? I got to set the Previous dot next to none, and that chops off the tail, and then the other thing I got to do is tell the tail, hey, you're over here now, okay? All right. So if what I'm deleting is the tail, let's do it. Previous dot next gets none, chops off the tail, and the tail, we got to update it. It's no longer what it used to be, it's now the previous node, all right? And that'll condense it down. All right, so now we've removed the tail, removing the tail, great. Okay, finally, else, the last scenario, removing from the middle. Well, oh, and the other thing I forgot to do, we got to decrement the size here, okay? So finally, finally, finally, right, our last scenario here. When we're removing from the middle, I guess I just need to draw some new boxes. When we're removing from the middle, a, and it doesn't, oops, it doesn't matter where in the middle, you know, just not the head and not the tail, that's, that's all we care about. So a will point to the b node, b will point to the c node, and I'll say that the c is the tail. All right, so say I'm removing b here, okay? According to our loop, curve will be here, previous will be here when we're done, okay? And what we wanted to do was we wanted to get the previous, next, this link to point to c, okay? Point to c node. So previous.next equals curve. This guy will then kind of chop off and now point here, okay? All right, that's going to be our last one. Previous.next gets curve.next. All right, man, and finally, can't forget to decrement the size, okay? So if that size minus equals one. Okay, now I will just say right now, this code is not concise, it is not optimized. You can make this stuff much smaller, but I want to get it right first. So let me run my test and see how we did. Okay, test remove on an empty list raised a value error, that's actually what we wanted. I need to implement a pen for my removed tests to work, so that's kind of annoying. Okay, so you have to implement these in the order in which they appear. Yummy. Do I implement it? Should I implement it? You probably want me to implement it because that would do the work for you, right? Sure, why not? A pen looks a whole lot like ad. So I got to put this in here. You know what? I'm going to do it fast. I'm not even going to tell you what I'm doing. If you can follow along, I say good for you. I am implementing the append method here, and I'm just going to put some code in there real fast. All right, so again, you have to implement append for the tests for remove to work, so I just copied some code in here real, real, real fast. You can go back and copy it yourself if you want, but please also understand what it does, because I will ask you coding questions about adding and appending and removing in Python. So I have a whole mess of tests for removing. Okay, here they are. And I see a lot of happy check marks. I see a lot of past. I did it. I did it. We did it together. So remove. Wow, what a crazy algorithm. All right, not so, not so. All right, it's going to get just a tiny, tiny bit crazier as well. So we've got one more method to talk about, which is the pop method. But actually, it's horrifically similar to the remove method. We'll talk about it a little bit next time. We're not going to code the whole thing, but we'll get you started with it. And again, finally with remove. Yeah, this is a big bear of a method. You can do this more efficiently, right? You can get this down to, let's see, I've got about one, two, three, four, 10, 12, about 20 lines. This one's a lot longer than that. You can, if you tweak and if you play, you can find that there's some duplication here and you can make it better. Same with this while loop. We can do this a better way. And in fact, I will show you the better way for doing this while loop because break statements are bad. Break statements are bad practice just in general. Okay, they're harmful. There's another way to write this loop. And I'll show you how to do it. So while cur, not equal to none. And we're just kind of going to flip this condition around. And cur.data is not equal to the item, right? Do this. Walk the list. Okay, so I've just kind of flip by flipping around, adding to this loop condition up here, cur.data is not equal to item. By changing this loop condition a little bit, I got rid of that nasty inside if else with the break statement. Again, break statements are bad. You should never need one. So just by flipping this thing around a little bit, I get the same behavior. All right, there we go. This also can be optimized a little bit. I'm not going to get into it. This is explicit. It kind of captures what we're trying to do. I've got some decent documentation here about the logic about what everything handles. I'm good with it. For this assignment, we're not caring about the most optimal algorithm, although in some cases I say methods have to have a big O of something implementation, big O of one. But other than that, we're not really caring about efficiency or cleanliness while the code should be clean. I don't care about how concise it is. All right, rambling now. Here's the code for remove. We will talk about pop next time. And that's going to be our last one.