 Alrighty, welcome back to 105, so if I am tired at all, this is the reason. Yeah, those are five puppies, so if I'm sleep deprived, that's why. All right, fun. So, talking about link lists, and we get to talk more about their implementation today. So, let us just dive into the code and solve our first one, because someone already identified this in Discord, which was great. So, let's say I have a link list. So, first off, any questions about anything we did yesterday? We had lots of fun, right? Link lists are cool, it's just pointers on pointers on pointers. All right, sweet. Today, yay, more link lists, more pointers, more fun. I think I said that yesterday too. So, if we do some link lists, we create a link list and let's say, we wrote that function insert front last time, that just created a new node, put it at the front. So, if I do that, let's say I create four nodes this time, and then we always say that we should free our memory once we're done using it. So, in this case, after printing my list, I'm done with it, so I can free the link list, which is basically just a location to store the head pointer for my link list. Then I can free node one, node two, node four, and exit. So, what is the bug with my program? Yeah, I didn't free number three, right? Kind of a pain in the, you know what? If we have a whole bunch of nodes, we forget to free just one and then everything unravels. So, first thing we should probably do is be like, oh, well, maybe I just write a function that frees that entire link list itself, frees every single element. So, maybe I go ahead, write a function that's called, I don't know, there's one called create link list. So, let's try calling it, I don't know, void free linked list. Now, it would take a link list type as a parameter by pointer. So, we can go ahead and actually use that address if we wanted to change the head or whatever, or actually free it. So, how would we go about freeing everything in this link list? Well, we have to be really careful so we don't try and use the memory after we free it. So, if we did something like just free link list, and then I was like, oh, well, maybe I need a node to keep track of the current node, I need to free. And I did something like, I initially set it at head. So, like, I did my looping thing before. If I do something like this, this is also wrong, right? So, this is a use after free. So, if I go ahead and let's see, it was free link list, let's get rid of all those individual calls. So, if I do something like this, this should not work, right, because, well, I have a free, and then all the memory associated with this link list pointer is freed. So, if I try to access one of its fields, so remember the arrow dereferences the link list first and then accesses this head field, which is the variable defined in the struct. If I do this and try and run it, well, then it works, oh, great. So, turns out it worked, but if I use valgrint, it would probably complain at me very loudly, saying that, hey, here's an invalid read of size six, I shouldn't have actually done that, because that's a use after free. So, I have to be very careful. After I get the pointer of the head, only then can I free it because then I'm done with that link list structure, I don't need anything, I just need to get the current value of head, and then I could go ahead and write my while loop like before, so while current does not equal null. So, in this case, can I just free current and then go current equals current next? Is that a great thing to do? Probably not, same issue again, but now this time I'm freeing the node and now in this line I'm trying to dereference it and then access its next field, so again, this is the same problem of use after free. So, I probably just, I'd really want to capture the value of next because I need it to go to the next element, but I also need to free the structure that has that next variable within it. So, to solve that, we just create a new temporary variable, we could call it next and capture current.next, then we can free current and then just use that variable to reset current to next and do something like that. And now, doing that, are we done? Yeah, looks pretty decent to me. Should iterate through everything, free every single node until the very end. So now, if I run that, if we double check, all right, now we have a bunch of unused variables because, well, we don't need to free them anymore. So, if we run valgrind, we get all heat blocks were freed, no leaks possible, six allocations, six freeze. It is all happy with us, it is not yelling at us at all. And as a bonus, we can actually get rid of some pointers, we actually don't need any of this, we can just simply delete it. So now our code looks like this. No memory leaks, we're all good. So, any questions about that? And starting to, I mean, there's a lot of pointers going on, but once we write the functions once, we can just use free link list, then we don't have to worry about iterating through them ever again, we just use our function. All right, any questions about that before we do more fun things? So it's just gonna be like all pointers, all link list today. And, wait, is lab nine released yet? Okay, so lab nine will have link list, we're like a week ahead of other sections, so you could, it'll probably don't quote me on this, but it should be do it like the last day of class since the other sections are behind, but we can go ahead, we can finish it long before. If you can, you can give it a read if you have any questions that are specific to that. But, the stuff I'll introduce today will probably answer that as well. So, that's our function that frees everything for us. Again, put it in the slide so you can have it. All right, our next task is going to be a fun one. It is going to be removing a node. So, in this, we're going to assume we have a pointer to node three there, and then we just want to remove node three from this list. So, what are the changes we need to make in order to remove node three from this list? So, do I need to change the head pointer? Oh, wait, yeah, way up there. Yeah, so the first step probably, if I want to remove node three, I have a pointer to it, while to actually remove it from the list, I have to take node two's next pointer and point it to node four instead of node three, right? I have to like look at the node behind me and move its next pointer to go to my next, right? So, yeah, that would be the first step is take node's two next pointer and point it to my next. So, they should both be pointing at node four and then that could be it, because now if I iterated through the list, I started head, I go to one, print one, go to two, print two, and then two leads me to four, so I would get one, two, four, three would be gone, right? If I want to be nice, maybe in node three I set next to null, so maybe I do that, so I just set next to null, clean it up, could do that if we want. Some people don't bother because it's not in the list anymore, but just in case we will set it to null, just to be super careful. So, yeah, so node three's next pointer we set to null because we're assuming it's not in the list anymore, but it doesn't really matter, we can just leave it its next pointing to four if we want because if we try and iterate through the list, we won't ever access node three anymore, right? We'll go from one to two to four. So, up to us whether or not we want to update it to null. Okay, so, to remove that, to think about it like in terms of what steps your computer would have to take, we can probably create a variable called previous, we can initialize it to null, and well, if we have a list that we only have head and then a pointer to node three, well the only way to get to node two is just by iterating from the head and trying to find node two, right? And another way to think of it is, well if I know the head of the list in node three, I can just keep a variable called previous that points to whatever the previous node is and then I can keep updating previous until I find node three which matches the pointer I have. In this case, previous should point at node two and then I can go ahead and change the pointers and all that fun stuff. So, that is our goal here. So, we're gonna iterate through the list so we find our node updating previous so that at least in this example, previous should be equal to node two and then once we find the node, well, we know the previous node, we can update the previous nodes next to point to our next and then we're effectively removed from the list. So, another thing to think of this are edge cases. So, what if the list is empty? What if the node is the head of the list? Well, if previous is null after the search, that means the node you are trying to delete is the head. So, instead of updating a next pointer, you have to update linked list head instead of the previous's next pointer because there is no previous and maybe the other edge case is that the node you're trying to delete isn't in the list. There's different implementations for this. Two valid things to do is you could just crash and just say, hey, you're not allowed to try and remove a node that's not currently in the list or you could just do nothing, you could detect the error. We'll do both today just for fun. So, let's try and write to the node remove function. So, get back here node four. All right. So, to write remove node, so we're given the linked list, that way we can access head and everything and then we're given the node we want to delete. So, if we follow the steps we kind of argued about before maybe I create a pointer called previous, set it equal to null and then like I had when I was iterating through the list to print all the values, well I could go ahead and have a node called current and that would, whoops, linked list. I can't type. So, I could set up to linked list head and then have my while loop that iterates through the loop. So, while current is not equal to null, I can go ahead and well I could update previous equal to current and then current equal to current next. So, that's the way I could go ahead, update the previous over and over and over again but while this will just iterate until I hit the end of the list but really I'm actually looking for a node, right? So, I'm actually looking for this node. So, I should probably stop this while loop while current does not equal this node. If current equals this node I want to stop this while loop because I want my previous to be the previous one to this node. So, another condition I can add to my while loop is just current does not equal node. So, in order to go through the while loop these both have to be true, right? So, I can't be at the end of the list so current is null and while I want it to stop if current equals a node that means I found it and I'm done. If I don't care about the case where I crash I could just have this condition and that's it if I really wanted to but we'll leave them both in for now. So, any questions about that? Okay, so what pointers should I update? So, we can go back real quick, let's see. So, first pointer I updated so in this case if we're moving three hopefully previous is equal to node two, right? So, what is the pointer update I make? So, this is previous and this is node. You wanna tell me which one I update? Yeah, so, if this is previous and this is node or current and if I want this at the end well then I have to update previous next to be equal to my next so I get that, right? So, in this case previous is two node is three, so previous is next so this should be equal to this is next so they're both pointing at the same thing. So, if I do that, let's see. So, previous next equals to just node next current next shouldn't matter. So, this should remove it, right? All right, well let's just try it out. So, here I'll give these names again so let's see let's call this node four so let's print the link list then we'll try to remove so hopefully if I do this what should my link list print on that second line here? Will I see one, two, three, four? Or something hopefully different? Yeah, one, two, three hopefully, right? So, let's walk through it step by step make sure it works this program's called list three. So, second time I print it I get one, two, three because I tried to remove node four, right? So, if I tried to remove node four let's see, like my picture, well node four it's previous was three so I would have updated threes next to be equal to fours next which is both null so they're just pointing that would effectively end the list so I would never reach four so I just get one, two, three. So, with our code am I done is this perfect code or do I have another edge case to consider? So, one worked let's make sure let's for fun we can give node three so like our example make sure it works so let's remove node three so now I remove node three my list says one, two, four so three got rid, I got rid of three so anything else I need to consider in this? Yeah. Yeah, so I could make node three point to null or another or whatever I'm updating so the nodes next is equal to null like that. Yeah, that's good. Yeah, the first node, yeah so we got a slight issue here so let's say we tried to remove node one we compile, we run it, boom, our favorite error is segmentation fault. Why did that happen? So, what's previous to one? Yeah, so we, so what happened? We currently set previous to null and then well current is equal to the link list head in this case it would be node one and then well we're trying to remove one so current would be equal to the node we're trying to remove so the while loop would break because it's the first one we wouldn't go into it so at this line previous is equal to null and then if we try to dereference previous which is what this is doing that dereferences null then we get a segmentation fault so we have to take into consideration the case where if previous is equal to null that's that corner case where the first element we're trying to remove is the head, right? So all I need to do is make the link list head equals to node.next and then only if previous is not null then I update the previous node so it should look like that, right? So let's make sure we work, boom, we work. All right, any questions about that cause that will probably be the hardest one although I'll show you a way to do it in less lines of code but it will probably hurt a little bit. So any questions about that before we carry on? Perfect, so yeah, again link lists are hard so if you have any questions feel free to ask whenever. So here's us removing it, here's, oh yeah, and I have another corner case because current could be null if this node is no longer in the list but we said we could crash, we could not but turns out if we want to do something like this if current is null aka we didn't find it then we could just return and do nothing and then if I try to remove node one twice or program doesn't do anything just carries on, keeps on working while if I didn't care about the case of the node not being in the list and it can crash I can simplify it a little bit like this I can just get rid of that check so if the node isn't in the list this loop will just keep on going forever until eventually we get current is equal to null like it reaches the end of the list and then it's going to try and dereference null and then it's going to have segmentation fault so if I do that this time, boom, I get a segmentation fault if I try and delete it, if it's not in the list if I just try and delete it once it should work no problem. So works now, two different ways to write it depends whether or not you want to crash if it's not in the list or just carry on and just kind of ignore it. So questions about that, that's lots of fun, all right. So that is the way people usually introduce it. We could think about this problem more indirectly so another way we could think about is we could check the address values for head and next until I find the node's address so then I know I'm essentially updating its pointer instead of going backwards and updating its next so I just get the address of its next pointer which might hurt the brain a little bit and then I update that it actually simplifies and removes that edge case so after I find my address well it's either stored in a head or a next pointer so I can just update that value to point to my next and I don't have to worry about any corner cases anymore. So I note this might be very confusing at first I'm throwing this in here because well the head developer of the Linux kernel that everyone's everything runs in likes it this way better and they are an expert in C so if you want more explanation for it there's a link in the slides but I will introduce it and try to explain it so I could rewrite it to just check all the pointers and why does this hurt? Well because I have to use a pointer to a pointer to a node why do I have to do that? Because I'm checking like the head and the next field of all the nodes so here I'm just looking for my address in one of those fields and I'm trying to update it so I could have a pointer to a pointer to the node that starts at the address of the linkless head and then in the while loop I could simplify it if I'm just this is equivalent of the other cases so while it's next or head is not equal to me I just keep going on and then I update it so I have to dereference p so that becomes just the node pointer and then I can take the access to the next field and then I can take the address of it to go to the next one. Boy does that look ugly, right? But really all that's doing let's see if I can try and illustrate it. Basically all that's doing in this case this case, yeah. What that's doing in this case is it's pointing to head and then it's checking, so if we're removing node three it's pointing to head and saying does head point to node three? No, okay, does this next point to node three? No, does this next point to node three? Oh, yes, well I can just update it I can just change that to point to node threes next and then I also don't have a corner case because if I want to remove node one while head's pointing to node one oh, well I can just change head which is just a pointer to a pointer I'm just changing the value I can have it point to node two and then I don't have a corner case anymore. It's weird but it works. If you like the corner case one better you can think of it that way because yeah, this is a pointer to a pointer. So this is what it looks like if I wanted to just get rid of the case where it's not found in the list I could actually delete this line if I wanted to and then to update it, well I just dereference p so that's either pointing to the head of the list if it's the first one or some next field of another node and I just update that to point to my next and then I'm done. So this one might take time to set in so you can go ahead, look at it again later or if you want to just ignore it completely you can do that and just do the other case which is this one which just more clearly illustrates that you're looking for the previous node and then changing its next pointer. The only difference is you have to keep track of this edge case where it's the first element of the list. All right, other useful functions. So we can just write a whole bunch of useful functions like getting the length of a list seeing if it's empty so we could write a few other functions so like if I wanted to I could write is empty and for is empty, well if the list is empty that's the same as the head just being null, right? It's not pointing to anything so that could be my boolean. Hopefully we remember those to check if the list is empty if I wanted to get the length of the list so like our equivalent array length macro before it works up now because it's a pointer, it always works. Well I could start off with the length is zero, create, just iterate over the list like we've been doing so far so create a variable called current set it equal to the list head and then have a while loop so while current does not equal null, length goes up by one and then we go to the next pointer until we're eventually done and then we return the length so this would return the length of the list we could write that recursively too, right? So the base case would just be null the length is zero otherwise my recursive case would be okay well one plus the length of the rest of the list, right? So could get practice right recursively if you really want I could also write is empty in terms of length so instead of checking if the head is null I could check if the length is zero means the same thing, right? So might wanna compose our functions that way doesn't really matter. All right, let's talk about insertion now which is a more fun case so say we're in this scenario where our link list we removed three so now we have one points to two points to four and we just have kind of node three just out in the wilderness and what about if we want to put node three back in the list so what if I wanted to insert it after node two? So how would I insert it after node two? Yeah, assuming node three is created. Yeah, yeah, so I need the next of the second node pointing to the third and whatever this was pointing to I need to make me point to it, right? So order here is going to matter two so if I update twos next to point to three now I can't point at four anymore so I probably need to update the other one first. Yeah, so first step I should do is if I wanna insert three after node two well I take whatever node two is pointing to next so in this case four and then node three points to four or two so now they're pointing to the same node and now instead of node two pointing to node four it should now point to me node three and now it is back in the list, right? So just two pointer updates if we were to write that. All right, so I have suffered retrograde amnesia help. What were those two pointers I had to update? So in that case what was I trying to do? I was trying to insert this node after this one. So yeah, so after's next is, yeah it's gonna be this node's new next, right? So in the case on the slide well in this case after would have been node two so node twos next nodes four so I need to make me point to that node as well so in code that would mean that my next is equal to after's next, right? And then after's next it is just the node itself, right? So in this case I just take it's next now my next is it's next and instead of it pointing to whatever it's next was it is now pointing to me, right? So I just, or you can think of this as you just like swooped in in line in somewhere you just cut in line, everyone enjoys those people, right? So let's see here. So to test that it worked I create that link list again so four, three, two, one then I remove node four so when I print it it should be one, two, three and then maybe I just create a new node and try and insert it after node two so after this point I should see one, two, seven, three, right? And then print the link list and free it so let's see if we work build insert after boom. So that seems to work as well, right? So I inserted after node two, node seven maybe I wanna check if it works at the end as a corner case so I could insert it after node three so insert after node three, in this case it should work it should just throw it at the end because node three is pointing to null so makes me also point to null and then makes node three point to seven so still works. So no other corner cases, insert after just two little pointer updates so with link list I find it easier like that diagram just to draw out the pointers and figure out what needs to change. The order you kinda have to look at the code and be like oh okay well I need after next so I can't update it until I use its value otherwise I lose it and typically that's what trips people up because it's really easy to lose a pointer if I update it in the wrong order we've seen numerous times if I just flip the two lines really bad things happen for fun we can flip these lines so if we flip these lines what's gonna happen yeah or no still confused yeah yeah so basically like the after nodes next so points to seven so like in this case let's go so three would point to seven and then sevens next would point to afters next which is seven so it should just be like one two three seven seven whoops I didn't compile it sometimes you need to compile things and then seven seven seven so yeah you will probably so if you see this while you're doing link lists you didn't write an infinite loop you just made a pointer point to itself recursively over and over again so just be wary when you do that because just flipping the lines of code gets really hairy I think that's what at least the second time we flip two lines of code and made point to itself and then it just print infinite looped over and over and over again so that's lots of fun so any questions about that all right then let's go for a challenge then so where do we have now so challenge so we want to insert a node before another node so let's assume it's set up like this so my link list has one pointing to three pointing to four and then I want to insert node two before node three yeah this should seem so I'll give you a minute this should seem familiar to what do we do first I forget yeah removing the node yeah it should look similar to removing the node so give you a minute to think about how we should do this but hint it looks pretty similar to the removing node so I will explain what I should do in this case you can be fairly vague and use the word previous and blah blah blah but I'm trying to take node two and insert it to node three so I have the link list so I know the head I have a pointer to three and I have a pointer to two which I would like to insert so what are my steps I need to do yeah yeah so so you said I need to find node three's previous so I need to find node one right and then I need to take that previous and make a point to me right if I'm inserting two so first step is I need to make its previous point to me and then my next should simply just point to well the node that I'm inserting before right my next should just be right next to node three and it should look like this after right so we said previous so we could kind of steal our operation or steal our implementation and do a nice little copy paste from remove node so same idea this time I'll just crash if it's not found cause I don't cause it fits on the slide better so here set current equal to head previous to null and then I'm looking for it so I'm looking for the node before the one I want to remove or insert myself before so I want to update previous so I update previous and then update current and then after I'm done this while loop I know that current is equal to before so previous is the previous node to before a lot of words that kind of mean the same thing but they're different nodes so in this case what was previous to node three was node one so this would be previous this would be before and then node would be two so I have to take the corner case again the previous is null then the head should point to me otherwise the previous is next should point to me and then my next should always point to the node I was inserted before right everyone having fun with pointer updates yet? alright that's why this is fun so why I showed you the indirect one is that if you want to do the clever solution and look really smart especially if you can actually explain this one you could actually write insert before and remove like this so in both cases I'm kind of looking for that next pointer I'm like searching for something so I could just write a function that returns a pointer to a pointer to a node and that will either point to like the head pointer I need to update or the next one I need to update and I can search for a node so as soon as I find it I just return a pointer to that pointer so I can dereference it and update the value fun but then that makes my remove node super short so after I find the node I want to remove so now it's pointing to some next field well I can just update it to point to that nodes next so it just skipped over that element and then I could update nodes next to be equal to null or I could just remove that line if I just wanted two lines and to be super clever my insert before also looks a lot shorter so I can find where the before is so now P points to the pointer that has a next or a head that I need to update so I can update it to point to me and then I point my next to before the node right, same thing but I can write both functions that fit on a slide without doing the previous or do anything so again you don't have to write your code like this I highly doubt the other sections will see this but if you can understand this it kind of looks oddly nice, right? It's like debatable whether or not you want to read this because even if it makes vague sense now I bet if you go to sleep and you wake up and then you look at this again you look at the first one you're like what in the blue hell is that guy doing but C programming is all about practice so if you come back and you can read it after 10 minutes then the next time you wake up you can read it again after five then you can read it again after like two and then suddenly it kind of like makes sense and you're like wow you great program bro or something I don't know, I don't know what you would say but oddly looks good so other functions we can write so we've done all of the hard ones so we could just write a function to insert a new node at the back because well before we got lost in the plot of all this we were trying to remove an element from the front and then move it to the back so we could write a function to insert it to the back so if I want to use that function again well I could and this one will create the node for me so I can create a new node and then use that find indirect to find null so it would find either the list head is null meaning it's empty or I'm at the last element like I'm at the end of the list so I can just update that pointer to point to me instead and that's all I have to do or if I wanted to write some more code that looks familiar in terms of the other well I could just write a function called insert end that would maybe be a bit nicer if I don't want to remember null would be like ending or adding to the beginning or the end like just the end of the list so maybe I make a function called insert end that takes the link list and the node to put at the end and then I could use that insert before with the null that would just put it at the end and then maybe I write a function called insert back that will just go ahead and create the node for us so I can just create the node and then insert it at the end and then return a pointer to the node just depends whatever you think is more useful so insert end as soon as you already have a node that exists insert back will just go ahead and make the node for you so any questions about those ones? All right fun again you can review this later like I said we're ahead of other sections so we don't have a lecture Friday so please you know you can ask on Discord email me anything you want and we can explain it more but just to wrap it up we can have another function that can just like remove and return the first node so remove front well it could just get the node that's at the link list head check the corner case that the list is empty so if the node does not equal null then well I have to make the head of the list point to its next so I need to skip it by one otherwise if node was null that just means the list is empty so I can just return null and just return node so now finally we can have our code that looks a bit nicer we can have our link list we can insert all of our things at the back so one, two, three, four so we can do it in order now might look a bit nicer instead of doing it in the reverse order like we did before print the link list now we can just remove the front of the list and get that node back so that should be one and then we can throw it to the end print the link list and then finally we have done it so now if we have this code and run it we see that well we took one and we put it to the back of the list and that was definitely easier than to raise right only took two lectures to do that instead of a bunch of assignment savings so there's like a lot of other reasons to use link lists this is not the most efficient one but we had fun with the journey right so we'll go over other reasons you want to use link lists so most of the time the data structure you use is either like array or list and take some experience to know which one to use when and there's also different tweaks you can do to link lists to make them faster for your uses so here let's so other things we could do whoops so other things we could do with the link list is maybe in that link list structure maybe instead of keeping a pointer to the front maybe I keep a pointer to the front and the back and that way I don't have to iterate through the list to access the back but of course it would be tricky to implement because we have a lot more corner cases so I would have to think if I need to remove the node and it's the only element in the list so it's both the front and the back I have to make sure that I've set both those pointers to null or if I add something I make sure that I set only if I add to the front I only update the front if I add to the back I only update the back and that way you wouldn't have to iterate through the list if I'm just like inserting at the end if you kept another pointer but we'll go over that later it's just more pointer fun, more edge cases more things we have to keep track of but we did a lot of work today so here's all the functions we wrote today so we sped run it we did a lot of pointer stuff but we wrote a lot of useful functions so we wrote freeing the link list we wrote checking if the link list is empty if it has no elements we wrote function to return the length or how many elements are in the list wrote a function to insert a node before another or after another one which was fairly easy that was two pointer updates the harder version of inserting before an element which looked like remove oh yeah and we also forgot we also wrote remove node too that's not even on here so we wrote an extra function then we wrote insert end so that's to put a node at the end insert back which created a node and put it at the end remove front we just, that returns a node we just had a good old fun today so you can enjoy your Easter break with that ask any questions like I said, email and figure that and yeah, don't be afraid because link lists historically the hardest topic or so I've been told so just remember, pulling for you we're all in this together