Hello! Welcome to the seventh lesson in our "Data Structures & Algorithmic Analysis" module.
In our last lesson, we saw how the LIFO (Last-In, First-Out) nature of a stack makes it the perfect tool for solving problems involving nested structures, like checking for balanced parentheses. We used the stack to match the most recently opened bracket, which was the core requirement of the problem.
Today, we shift our focus to the stack's counterpart: the queue. This lesson will explore the queue's fundamental FIFO (First-In, First-Out) principle. Our goal is to apply a queue to solve problems where fairness and processing order are key, such as managing shared resources. Understanding when to use a queue is crucial for designing predictable and fair systems, a cornerstone of the architectural mindset you're building.
1. The FIFO Principle: First Come, First Served
A queue operates just like a line at a store: the first person to get in line is the first person to be served. In computer science, this "First-In, First-Out" (or "First-Come, First-Served") principle is fundamental for managing tasks, data, or requests in a fair and orderly sequence.
- Enqueue: The operation to add an element to the rear (or back) of the queue.
- Dequeue: The operation to remove an element from the front (or head) of the queue.

The following video offers a great explanation of this principle with several real-world and programming examples.
Queue and FIFO/FCFS explained in 10 minutes + a bonus task 📣📣 (Data Structures course)
The video 'Queue and FIFO/FCFS explained' by CodeBeauty provides clear analogies for how queues work and when they are used in software.
Watch from 02:00 to 04:45. Pay attention to the examples given, such as a printer queue and processing online orders. These are classic scenarios where FIFO processing is essential.
As you saw in the video, queues are invaluable when a system receives requests faster than it can process them. By placing incoming requests in a queue, you ensure that they are handled in the order they arrived, preventing data loss and ensuring fairness. This pattern is common in web application backends, for example, when handling background jobs like sending emails or processing file uploads.
2. Application 1: Managing Shared Resources
One of the most common uses for a queue is managing access to a limited resource, like a printer, a CPU, or a network connection. This is often called scheduling. A queue ensures that requests for the resource are handled fairly.
Let's explore a practical example: a print spooler system. When multiple users send documents to a single printer, the spooler uses a queue to line up the print jobs.
The following resource provides an excellent Python implementation of a print spooler. While the code is in Python, the logic is universal and clearly demonstrates the application of a queue.
🚀 Queue Implementation: FIFO Data Structure - Tutorial | Krython
The article 'Queue Implementation' from Krython includes a hands-on exercise to build a print spooler. We will focus on its solution to see a queue in action.
From the 'Hands-On Exercise' section, click to expand the 'Solution'. Study the code for the PrintSpooler class. Focus on these methods: add_job(): How does it add regular jobs to the queue (self.regular_queue.append(job))? get_next_job(): How does it retrieve the next job from the front of the queue (self.regular_queue.popleft())? process_queue(): How does this method orchestrate the FIFO processing of jobs?
The print spooler code demonstrates the core FIFO pattern:
- Jobs are enqueued (added to the back) as they arrive.
- The system dequeues (removes from the front) one job at a time to "print" it.
- This continues until the queue is empty.
This simple mechanism guarantees that the user who submitted their job first gets it printed first, which is the behavior users expect.
You might have noticed that the example also handles "urgent" jobs using a priority_queue. This is a more advanced type of queue that we will build and analyze in a later module when we cover heaps. For now, the key takeaway is how the standard queue ensures fair, ordered processing for the non-urgent jobs.
3. Application 2: Graph Traversal with Breadth-First Search (BFS)
Queues are not just for managing external resources; they are also a critical component of many fundamental algorithms. A prime example is Breadth-First Search (BFS), an algorithm used to traverse a graph or tree.
BFS explores a graph layer by layer. It starts at a given node, visits all of its direct neighbors, then visits all of their neighbors, and so on.
A queue is the perfect data structure to manage this "layer by layer" exploration. The algorithm works as follows:
- Add the starting node to a queue.
- While the queue is not empty:
a. Dequeue a node. Let's call it thecurrent_node.
b. Process thecurrent_node(e.g., print its value).
c. Find all unvisited neighbors ofcurrent_nodeand enqueue them.
Because the queue is FIFO, nodes at the same "level" (distance from the start) are processed before any nodes at the next level.
The following video provides an excellent visualization of how a queue powers the BFS algorithm.
Breadth First Search (BFS): Visualized and Explained
The video 'Breadth First Search (BFS): Visualized and Explained' by Reducible clearly shows the role of the queue in maintaining the order of nodes to visit.
Watch from 02:38 to 10:19. First, focus on how the queue is used to generate the BFS traversal order (until 05:10). Then, observe how this same logic is applied to solve a practical problem, 'Flood Fill' (the paint bucket tool in image editors), which is cleverly framed as a graph problem.
The "Flood Fill" example is a great illustration of architectural thinking: reframing a seemingly unrelated problem (coloring pixels) into a classic graph traversal problem that can be solved with a standard algorithm (BFS) and data structure (queue).

4. A Note on Performance: Circular Buffers
In the previous lesson on implementing queues, we discussed using either an array or a linked list.
- Linked List:
enqueueanddequeueare both . - Simple Array/List:
enqueue(appending to the end) is often , butdequeue(removing from the beginning) is because all subsequent elements must be shifted.
To get performance for both operations with an array-based implementation, we use a technique called a circular buffer (or ring buffer). Instead of shifting elements, we just move pointers for the front and rear of the queue. When a pointer reaches the end of the array, it simply "wraps around" to the beginning.
This is achieved using the modulo operator (%). For an array of size S, the next index is calculated as next_index = (current_index + 1) % S. This ensures the index always stays within the array bounds and creates the circular effect.
This approach is highly efficient because it keeps data in a contiguous block of memory, which is very friendly to modern CPU caches. Compared to a linked list, which can have nodes scattered all over memory, a circular buffer often provides significantly better real-world performance. This is a crucial trade-off to consider when designing high-performance systems.
Test your understanding!
You are designing a web browser's history feature. When a user clicks the "Back" button, they should be taken to the most recently visited page. When they click "Forward," they should be taken to the page they were on before they clicked "Back."
Which data structure(s) would be most appropriate for managing the "Back" and "Forward" history? Explain your choice in terms of LIFO and FIFO.
Show answer
This is a classic problem that is best solved using two stacks, not a queue.
-
"Back" History: A stack is used to manage the pages you can go back to. When you visit a new page, you
pushit onto the "back" stack. When you press the "Back" button, youpopfrom this stack to get the most recently visited page. This is a perfect example of LIFO behavior. The page you just came from is the first one you go back to. -
"Forward" History: A second stack is used for the "forward" history. When you press "Back," the page you were just on is
poppedfrom the "back" stack andpushedonto the "forward" stack. When you press "Forward," youpopfrom the "forward" stack.
A queue would be inappropriate because its FIFO nature would mean the "Back" button would take you to the first page you visited in your session, not the last.
Conclusion
In this lesson, we've explored the queue's essential FIFO property and its applications in solving problems that require ordered processing.
Key Takeaways:
- Queues enforce a First-In, First-Out (FIFO) order, ensuring fairness and sequence preservation.
- They are commonly used for resource sharing and scheduling, such as in print spoolers, task managers, and processing background jobs.
- Queues are a fundamental component in algorithms like Breadth-First Search (BFS), where they manage the exploration of nodes layer by layer.
- The choice between a stack (LIFO) and a queue (FIFO) is determined by whether a problem requires processing the most recent item or the oldest item.
Next Steps
So far, we have focused primarily on the time complexity of our data structure operations. However, how much memory an algorithm uses is an equally important consideration in system design. In our next and final lesson of this module, we will formalize the concept of space complexity and analyze the memory trade-offs of the data structures and algorithms we've learned.