 Hello everyone, in this video we are going to introduce our next abstract data type and this is one that unlike the stack you've probably encountered in your natural life. And also is just super critical in computing applications, shows up absolutely everywhere and at least is pretty easy to understand I think. And that is the concept of the queue and a queue is an abstract data type where addition happens at one end, so we're going to have a collection of items once again. Addition is going to happen at one end and we remove from the other end. So it's a line, right? It's a line at Burger King and McDonald's at the bank at the DMV wherever you are, right? It's a queue. It's a line. And things in the queue, items that we put in the queue get serviced in the order they were placed in it, right? We call that first in, first out behavior, FIFO. The first thing into the queue is the first thing out. That's a fair queue, right? Compare that to the stack. Everything happened at one end of the stack called the top. That was last in. The first thing you put in is the last thing that goes out. The queue is opposite of that, right? We all know what queues are. We all know what they are. And it turns out they solve a whole bunch of problems in computing. And kind of the purpose of the queue is fairness, right? We want to process things in the order they were received. First come, first serve, right? So the printer queue, right? If you print a whole bunch of stuff at once or a bunch of different documents in a row from your computer, they come out in the order that the printer received them. The printer has a queue of things that it does. Keystrokes, right? It actually takes some time for your computer to process every keystroke. And usually it processes them instantaneously and you don't even notice it. But you have all encountered cases where you're like typing and then something lags your computer. It's doing some heavy crunching. Your hard disk is making a whole bunch of noise and you're like stuck there. And then all of a sudden a whole bunch of keys appear in a row, right? Well, luckily for you, the computer, the operating system is putting those keystrokes in a queue. Otherwise, they would just kind of get all jumbled together, right? And the other big place where you encounter queues are in network applications. So your router, for example, and the router at your institution, wherever you are, the network traffic that comes in and that goes out is being processed through a queue, right? And it's got to go, you know, the router has to determine whenever, let's say you're on campus and there's 10,000 people hooked up to the network and they're all watching you too. That data is streaming in over the internet and through a router. The router has to determine which computer on its network, you, your buddies, the person on the other side of your buddy, whose data goes to who. Well, it determines that through a queue. The data comes in and it very quickly snaps it out and puts it along, but it does take a couple seconds. You know, it takes some amount of time for it to figure out where it goes. It processes the data in the order it was received, okay? So queues, they're kind of natural, they're useful in a lot of application. Queues are queue data, right? A queue is a collection of items and we typically store those items in a list. So in your next homework assignment, you are going to be implementing a queue data type and under the hood, the data structure will be an array-based list. Queues are very simple in the behavior that they expose. You're going to have a constructor. You're going to have an enqueue, enqueue an item, put it at the back of the queue, and a dequeue item, pull the thing off the front, add to the rear, pull off the front. That's it. You are not operating anywhere in the middle, right? Queues are not concerned with the middle of them. Just whoever is at the front of the line and then we add people to the back. That's it. Okay? Also be able to ask, is our queue empty and how many items are in it? All right. So let's go over to our worksheet. I'm going to switch over to my whiteboard and we're talking about this right here, the queue handout. So pull this up, we're going to work through it just to make sure that we understand conceptually how queues work. Conceptually they're not too bad. They make mostly perfect sense, I think, but let's walk through the examples here. All right. I'm going to get myself in front of my whiteboard without knocking things over. Okay. So step one, we have queue gets the queue constructor. So that's just saying make for me an empty queue, okay? Nothing there. Nothing yet. All right? Nothing. I know. On to step two, we are going to end queue the number 45, this is step two. Now conceptually here, queues can hold whatever, right? Any element. I'm just happening to put numbers in this one for whatever reason. It could be people, it could be bank account objects, it could be whatever, okay? So when I end queue something in the list, okay, I'm just going to write down what's in there. Now a queue, the queue has the notion of a front and a back, right? So right now the front, the person at the front, the item at the front of the queue is the number 45 and the item at the rear is also the number 45, right? So when we talk about queues and we're visualizing queues, know where the front is and know where the rear is, all right? That'll distinguish it from when you're visualizing, say, a stack because a stack only knows where the top is. All right. Step three, end queue the number 10. So when we add the number 10 to our queue, we get rid of, we're going to say that 10 is coming after 45 and now the rear is pointing to 10, okay? So 45 is the front, 10 is the rear and actually, you know, the queue doesn't care like how these things are stored or where they are necessarily and the user of the queue doesn't care. It's just who's the front and where's the rear, that's all, all right? Number four, end queue 9999, okay? So I'll draw it down here and it doesn't matter but there is an ordering, okay? Then we've got to update where the rear is. I always end queue at the rear of the queue, end queue happens at the rear, okay? Now we need to dequeue, step four, we're going to dequeue here, all right? Dequeuing removes and returns the item at the front of the queue, well, who's the front of the queue? Well, conceptually, the front of the queue is 45, okay? So dequeuing, we're on, oh, I'm sorry, we're on step five now, dequeuing, we are going to remove 45, right? And return it, so I'll kind of say 45, you know, with a little arrow that's going to be my sign that I'm returning it and I need to remove them, okay? Well, when I remove 45, who becomes the front of the queue? That's the question, right? Well, the front of the queue, queue's behavior says the next person, the next one that was added after 45 is going to be processed next. First come, first serve, we process them in the order they were receiving. So 45 goes away and the front of the queue is now 10, okay? All right, let's dequeue again. If we dequeue, again, step six, okay, we're on to step six now, who do we dequeue? Well, we dequeue the front of the queue, well, the front of the queue this time is 10. So we're going to remove and return 10, so 10 is gone out of our queue and now we're back to a case where we only have one item in the queue and that's 9999, okay? All right, so finally, we, step seven, we enqueue again. Step seven, we are going to enqueue the number six, where do we enqueue? We enqueue by adding to the queue after the current rear of the queue. The current rear of the queue, there's only one item. The front and the rear are the same thing. So just, you know, think about a line, first come, first serve, number six is going to get in line behind 9999 and then we update the rear to point to six, okay? So that's it for a queue, conceptually, very simple, right? No big deal. You are going to have to implement a queue in code. As I mentioned, I'm not going to switch over to code right now, but as I mentioned, you are going to use the list, the array-based list, as the data structure for a queue, okay? Because list is general purpose, you can put things in, add things, remove things, you can do all sorts of wonderful things with a list. But for the user of your queue, they don't care about all those other things. The queue only cares about enqueuing and dequeuing and how big it is. That's it, okay? So we're going to hide the list and use it as a data structure. Now, when we think of lists or array-based lists in particular, you know, you usually think of them like this, it's a contiguous block of stuff. And these are the indices, right? The array indices, list indices, indexes indices. And then you've got data in here. What data do you have, you know, doesn't matter, right? It doesn't really matter. But let's, I'm going to put, I'm just putting some letters in here as my data, okay? Now, we want to model or implement a queue using Python's array-based list as the data structure. We have to make a fundamental decision at this point. We have to decide, if we're going to use this list, what is going to represent the front of the queue and what is going to represent the rear of the queue, because I need to know those things in order to enqueue and dequeue properly, okay? I'm going to remove the item from the front when I dequeue and I'm going to add items or append items to the end whenever I enqueue. But you have the option, do you want the front of the queue to be the beginning of the list, index zero? Or do you want the front of the queue to be the end of the list, index, you know, length of the list minus one? It's up to you, you have to figure out. Your goal in computing is to be the most efficient, right? So, is there one, which one of these, the beginning of the list, index zero or index negative one, that is going to give me better efficiency for enqueue and dequeue? Well, let's think about it for a second, right? Let's say I want to use, I'm going to take these indices away just to give me some more writing room. Let's say I choose the front of the queue to be index zero, right? And the rear to be down here, right? And that kind of makes sense, right? Maybe your first instinct to do it that way. Okay, so I dequeue, let's talk about enqueue first. If I'm enqueuing and this is my queue, okay, this is the data structure that holds my queue, it's a list, it's a Python list, this is the data structure inside my queue, okay? I have a queue class, this is going to be my data inside of it. Where do I, when I call enqueue, I want to put things at the rear of the list, okay? How do I add things to the rear of a list? Well, if the rear is down here, I can call list dot, how do I add something to the end of a list? I call list dot append. Apologize for my handwriting because it's hard to write on this white board, it's not very stable. Anyway, list dot append, we'll put data here. We like list dot append because what is its big O? Well, we talked about this a couple of times, the big O of list dot append is big O of 1. That's good, that's as good as we're going to get. But now I need to dequeue, when I dequeue, right, I need to dequeue from the front. The front is up here. How can I remove items from the front of a Python list, of an array-based list? Well, there's basically two ways, but the one I want to use is I need to pop. Remember, there's a pop method and I want to call pop zero. Popping zero says take the item at index zero in front of the queue, in front of the queue should be index zero, remove it and return it. If you go back to our discussion, though, on pop, what does popping do? Well, an array-based list needs to be a contiguous block, so there can't be any gaps here. So when you pop this guy out, when you remove him, what's going to happen? All these guys will shift down a slot. And as we talked about in the array-based list lecture, that's expensive. We've got to shift all of these guys. So popping zero is a big O of n operation. Okay, so my dequeue is big O of n and my nqueue is big O of 1. That's not great. I mean, it's okay. Big O of n isn't terrible, but big O of 1 is way better. Okay, so could we do better? Could we do better if we treated beginning, and I'll write it down here, if we treat index zero in our list as the rear and index n minus 1 or minus 1 as the front? Would it get better if we did that? Okay, well, our problem with dequeuing, remember we dequeue from the front always. We have to dequeue from the front. And in fact, why don't I, I'll just make it obvious. You have to, when you dequeue, you dequeue from the front. When you nqueue, you nqueue at the rear. Okay, so my problem before was my dequeue was big O of n. Okay, all right, well, now I can actually dequeue in big O of 1 time. How do I do it? Well, Python lists have this pop method, just plain old pop, right? Plain old pop, no argument given. When you plain old pop, you're removing from the end of the list, removing and returning the last item at the end of the list. And go back to our array-based list discussion. Popping from the end of the list is big O of 1. You use some math to find the last item in the list because you know how big the internal elements of a list are and you know how long the list is. You jump immediately to the end of the list using some arithmetic and you remove and return the item. Easy, right, easy for the computer anyway. But now I'm nqueuing. I nqueue at the rear. Conceptually, that's easy. But in my data structure, the rear of the list is index zero. Well, how do I put an item into a list, a Python list at index zero? My method for that is insert, right? There's an insert method on Python. Let me say list.list.insert, try and emphasize the fact that I'm using list methods here. And I'm sorry that my whiteboard is getting out of control. If I do list.insert at index zero, like again, list.insert, that's something Python knows how to do, the value, that will put it here, but then what's got to happen? Well, anything that was already here in these spots, what's got to happen to them, they got to shift down. You got to move you here, you here, you here, you here, and then my new value, whatever that is, is going to go into the first slot, okay? The shifting, once again, I've got to move every item. I've got to copy it. That makes this big O of n, okay? So, not an ideal scenario. Let me switch back to my slides here real quick. Great. So, whenever you are using a Python array-based list, you cannot get both nq and dq to be big O of 1. Unlike the stack, remember the stack, we could push in, we could pop in big O of 1 time, that use a Python array-based list, and that is important. Because we are using an array-based list, one of these guys, either nq or dq, must be big O of n. Now, that's actually a problem. If you think about a queue that might exist on a router, for example, that's processing millions or billions even of packets per second, trillions potentially, packets per second, it can't be slow. No operation can be slow there. So, a array-based list is maybe not appropriate for certain applications there, okay? That's a limit of the data structure that we are using. Now, in your next assignment, you're going to make queues and stacks and another ADT. After that, our next assignment, you're going to make a new data structure. It's going to be called a linked list and it's going to overcome this problem. It will have some downsides to it, but you will be able to make a queue using a linked list, a different data structure where both nq and dq is a big O of 1 operation. And that's a good thing. All right, so we will talk about one more abstract data type in the next video and then we'll send you off to code these things.