Learning Outcomes
At the end of this lecture, you’ll be able to:
- Explain and trace the core operations of Queue (enqueue, dequeue, front, empty).
- Describe the difference between
enqueue
anddequeue
. - Implement the core operations of Queue efficiently (array based and linked base).
- Explain why an efficient linked implementation of Queue requires a tail pointer.
- Explain why an efficient array based implementation of Queue can logically be viewed as a circular data structure.
- Define Steque, Quack, and Deque ADT.
Lecture Plan
In this lecture, we'll cover the following lessons:
- Queue Abstract Data Type
- Queue Interface
- Linked Implementation of Queue⚡
- Trace Linked Implementation⚡
- LinkedQueue⚡
- Array Implementation of Queue⚡
- Trace Array Implementation⚡
- ArrayQueue⚡
- Restricted Data Structures: Mixing it up!⚡
Lessons marked with ⚡ contain exercise/activity.
Downloads
Queue Abstract Data Type ↗
A queue ADT supports two main operations:
- enqueue which adds an element to the data structure
- dequeue which removes the least recently added element that was not yet removed
The order in which elements are removed gives rise to the term FIFO (first in, first out) to describe a queue.

It helps to visualize a queue as an ADT where elements are removed from the front but added to the back, analogously to when people line up to wait for goods or services.
A queue has a variety of applications such as:
- Serving requests on a shared resource, like a printer, CPU, etc.
- Any application that follows a “first come, first served” policy, e.g. customer-service, call-center/ticketing system, etc.
- Whenever it is necessary to preserve sequencing order
Resources
- Wikipedia entry on Queue
- HAckerRank YouTube video on Stack & Queue
- VisuAlgo visualization and interactive demo of lists
Queue Interface ↗
Here is the Queue
interface which we use in this course:
/**
* Queue ADT.
*
* @param <T> base type.
*/
public interface Queue<T> {
/**
* Adds a new element to the back of this queue.
*
* @param value to be added
*/
void enqueue(T value);
/**
* Removes the element at the front of this queue.
*
* @throws EmptyException when empty() == true.
*/
void dequeue() throws EmptyException;
/**
* Peeks at the front value without removing it.
*
* @return the value at the front of this queue.
* @throws EmptyException when empty() == true.
*/
T front() throws EmptyException;
/**
* Checks if empty.
*
* @return true if this queue is empty and false otherwise.
*/
boolean empty();
}
Make note of enqueue
and dequeue
: one adds the other removes. Also, there is a front
which allows you to peek at the front of the queue without removing the front element.
Linked Implementation of Queue ↗
A singly linked list can be used to implement the Queue ADT efficiently as long as it has a tail
pointer (as well as the head
pointer). The tail
pointer is a reference variable pointing to the other end (opposite of head
) of the queue.
Exercise Think about how to implement a queue using a singly linked list such that the core operations are constant time.
Operation | How? | Time |
---|---|---|
enqueue | $O(1)$ | |
dequeue | $O(1)$ | |
front | $O(1)$ | |
empty | $O(1)$ |
Solution
The front of the queue would be the HEAD node of the singly linked list. The back of the queue would be the TAIL node of the singly linked list. Every time we want to enqueue
an additional value to the queue, we would create a new node and set it as the new TAIL node. Then when we want to dequeue
a value from the front of the queue, we set the HEAD to the current HEAD node’s .next
. As such, the HEAD node will always be at the front of the queue, and calling front
would return the value of the HEAD node.
Operation | How? | Time |
---|---|---|
enqueue | prepend the list and update the tail | $O(1)$ |
dequeue | delete from front: head = head.next | $O(1)$ |
front | return head.data | $O(1)$ |
empty | check if head is null | $O(1)$ |
Trace Linked Implementation ↗
Think about a linked implementation of queue such that the core operations are constant time.
Exercise Complete the table below: update the linked list as you trace the operations; show value returned if any.
Operation | Linked List | Value Returned |
---|---|---|
enqueue(1) | HEAD -> (1) <- TAIL | |
enqueue(2) | HEAD -> (1) -> (2) <- TAIL | |
enqueue(3) | ||
dequeue() | ||
enqueue(4) | ||
dequeue() | ||
enqueue(5) | ||
front() | ||
dequeue() | ||
front() |
Solution
Operation | Linked List | Value Returned |
---|---|---|
enqueue(1) | HEAD -> (1) <- TAIL | |
enqueue(2) | HEAD -> (1) -> (2) <- TAIL | |
enqueue(3) | HEAD -> (1) -> (2) -> (3) <- TAIL | |
dequeue() | HEAD -> (2) -> (3) <- TAIL | |
enqueue(4) | HEAD -> (2) -> (3) -> (4) <- TAIL | |
dequeue() | HEAD -> (3) -> (4) <- TAIL | |
enqueue(5) | HEAD -> (3) -> (4) -> (5) <- TAIL | |
front() | HEAD -> (3) -> (4) -> (5) <- TAIL | 3 |
dequeue() | HEAD -> (4) -> (5) <- TAIL | |
front() | HEAD -> (4) -> (5) <- TAIL | 4 |
Resources
- USFCA interactive demo of Queue (Linked List Implementation)
LinkedQueue ↗
Exercise Open the starter code and complete the implementation of LinkedQueue
. (Do this at home!)
Solution
Please check the posted solution.
Array Implementation of Queue ↗
We want to implement the Queue
interface using an array as an internal data storage.
Exercise Think about how to implement a queue using an array such that the core operations are constant time. Assume the array is sufficiently large.
Operation | How? | Time |
---|---|---|
enqueue | $O(1)$ | |
dequeue | $O(1)$ | |
front | $O(1)$ | |
empty | $O(1)$ |
Solution
Initialize two variables front
and back
to zero. When you equeue
, add to the back. Use the back
variable to index into the array. Then increment it. When you are asked for front element, simply return arr[front]
. When you dequeue, simply increment front
.
Since you remove from the “front” of the array, then there will be empty positions at the front. So, when the back
variable reached the end of the array, it can wrap around it and write to the (actual) “front” of the array, to positions that were removed earlier.
This gives rise to a logical view of array being a circular data structure.

You can also dynamically grow the array when back
reaches front
.
Operation | How? | Time |
---|---|---|
enqueue | data[back] = value and back = ++back % length | $O(1)$ |
dequeue | front = ++front % length | $O(1)$ |
front | return arr[front] | $O(1)$ |
empty | check if numElement == 0 | $O(1)$ |
The % length
is a trick we use to reset the index when it reaches the length of the array. We could rewrite it as
font = front + 1;
if (front == length) {
front = 0;
}
The example above replaces front = ++front % length
. The same idea can be applied to updating back
variable.
Resources
- Data structures: Array implementation of Queue on YouTube.
- Using an Array to represent a Circular Queue on YouTube.
Trace Array Implementation ↗
Think about an array based implementation of queue such that the core operations are constant time.
Exercise Complete the table below: update the array values at indices 0
, 1
, 2
, 3
, and 4
as you trace the operations; show value returned if any.
Operation | [ 0 ] | [ 1 ] | [ 2 ] | [ 3 ] | [ 4 ] | Return Value |
---|---|---|---|---|---|---|
enqueue(1) | ||||||
enqueue(2) | ||||||
enqueue(3) | ||||||
dequeue() | ||||||
enqueue(4) | ||||||
dequeue() | ||||||
enqueue(5) | ||||||
front() | ||||||
dequeue() | ||||||
front() |
Solution
Operation | [ 0 ] | [ 1 ] | [ 2 ] | [ 3 ] | [ 4 ] | Return Value |
---|---|---|---|---|---|---|
enqueue(1) | 1 | |||||
enqueue(2) | 1 | 2 | ||||
enqueue(3) | 1 | 2 | 3 | |||
dequeue() | 2 | 3 | ||||
enqueue(4) | 2 | 3 | 4 | |||
dequeue() | 3 | 4 | ||||
enqueue(5) | 3 | 4 | 5 | |||
front() | 3 | 4 | 5 | 3 | ||
dequeue() | 4 | 5 | ||||
front() | 4 | 5 | 4 |
Resources
- USFCA interactive demo of Stack (Array Implementation)
ArrayQueue ↗
Exercise Open the starter code and complete the implementation of ArrayQueue
.
Notice the constructor of ArrayQueue
does not take in a parameter for the size of the array. Feel free to initialize an array with an arbitrarily chosen capacity.
Solution
Please check the posted solution.
Restricted Data Structures: Mixing it up! ↗
We can mix and match the operations of Stack and Queue to build up new data structures! Here are a few that you may come by in various references.
Steque ADT
A stack-ended queue or steque is a data structure that supports push
, pop
, and enqueue
. Here is an example interface:
// A stack-ended queue.
public interface Steque<T> {
// is empty?
boolean empty();
// adds a new element to top of Steque.
void push(T value);
// removes and returns top element.
T pop() throws EmptyException;
// adds a new element to bottom of Steque.
void enqueue(T value);
}
So, you add from both ends but remove from one end. To better understand this ADT, consider the following sequence of operations and the schematic representation of steque after each operation (top of the steque is to the left):
steque.push(1); // [1]
steque.push(2); // [2, 1]
steque.enqueue(3); // [2, 1, 3]
steque.enqueue(4); // [2, 1, 3, 4]
steque.pop(); // [1, 3, 4]
Quack ADT
A queue-ended stack or quack is a data structure that supports push
, pop
, and dequeue
. Here is an example interface:
// A queue-ended stack.
public interface Quack<T> {
// is empty?
boolean empty();
// adds a new element to the top of Quack.
void push(T value);
// removes and returns top element.
T pop() throws EmptyException;
// removes and returns bottom element.
T dequeue() throws EmptyException;;
}
So, you add from one end but remove from both ends. To better understand this ADT, consider the following sequence of operations and the schematic representation of quack after each operation (top of the quack is to the left):
quack.push(1); // [1]
quack.push(2); // [2, 1]
quack.push(3); // [3, 2, 1]
quack.push(4); // [4, 3, 2, 1]
quack.pop(); // [3, 2, 1]
quack.dequeue(); // [3, 2]
Deque ADT
Double Ended Queue (Deque) is a limited access data structure that allow insertion/removal at either end in $O(1)$.

- Deque is a stack if you always add/remove at one end.
- Deque is a queue if you always add to one end and remove from other.
Here is an example interface:
// A double-ended queue.
public interface Deque<T> {
boolean empty();
T front() throws EmptyException;
T back() throws EmptyException;
void insertFront(T t);
void insertBack(T t);
void removeFront() throws EmptyException;
void removeBack() throws EmptyException;
}
The data structures above are not as widely uses as Stack and Queue (especially the first two). We leave it to you as an (unsolved) exercise to implement their operations.