In computer science, data structures are ways to organize and store data so that it can be accessed and modified efficiently. One important category is linear data structures, where elements are arranged in a sequence, one after another.
Examples of linear data structures include arrays, linked lists, stacks, and queues. Each has its unique way of adding, removing, or accessing elements.
In this section, we focus on queues, a linear data structure that follows a special rule for adding and removing elements called First In, First Out (FIFO). This means the first element added to the queue will be the first one to be removed.
Think of a queue as a line of people waiting at a ticket counter. The person who arrives first stands at the front and is served first, while new people join the line at the rear. This simple, everyday example helps us understand how queues work in computers.
Queues are widely used in computer science and appear frequently in competitive exams because they model many real-world processes such as task scheduling, buffering, and resource management.
A queue is a collection of elements that supports two main operations:
The key characteristic of a queue is the FIFO (First In, First Out) principle. This means that the element inserted first will be the first to be removed, just like people standing in a queue at a bus stop.
Elements enter the queue at the rear and leave from the front, maintaining the order of arrival.
Queues support several fundamental operations that allow us to manage the data inside them efficiently. These operations are:
This operation adds an element at the rear of the queue. For example, if a new customer arrives at a bank, they join the end of the line.
This operation removes an element from the front of the queue. Continuing the example, the customer at the front of the line is served and leaves the queue.
This operation returns the element at the front without removing it. It helps to know who is next in line without changing the queue.
Checks whether the queue has any elements. If the queue is empty, no dequeue operation can be performed.
Checks whether the queue has reached its maximum capacity (important in fixed-size implementations like arrays).
graph TD Start[Start Operation] --> CheckFull{Is Queue Full?} CheckFull -- Yes --> RejectEnqueue[Reject Enqueue] CheckFull -- No --> Enqueue[Add element at Rear] Enqueue --> End[End Operation] Start --> CheckEmpty{Is Queue Empty?} CheckEmpty -- Yes --> RejectDequeue[Reject Dequeue] CheckEmpty -- No --> Dequeue[Remove element from Front] Dequeue --> EndThere are several variations of queues designed to solve different problems or improve efficiency:
| Type | Insertion Point | Deletion Point | Key Features | Typical Applications |
|---|---|---|---|---|
| Simple Queue | Rear | Front | Linear, FIFO, fixed start and end | Basic task scheduling, buffering |
| Circular Queue | Rear (wraps around) | Front (wraps around) | Efficient use of space, avoids "false full" | CPU scheduling, resource management |
| Priority Queue | Based on priority | Highest priority element | Elements dequeued by priority, not arrival order | Emergency systems, print spooling |
| Deque (Double Ended Queue) | Front and Rear | Front and Rear | Insertion and deletion at both ends | Undo operations, palindrome checking |
Queues can be implemented using different data structures, each with its own advantages and disadvantages.
In this method, a fixed-size array is used to store queue elements. Two pointers or indices, front and rear, track the positions for deletion and insertion respectively.
Advantages: Simple to implement, fast access.
Disadvantages: Fixed size leads to wasted space when elements are dequeued; requires shifting or circular logic.
Here, each element is a node containing data and a pointer to the next node. The queue maintains pointers to the front and rear nodes.
Advantages: Dynamic size, no wasted space.
Disadvantages: Requires extra memory for pointers, slightly more complex.
Step 1: Initially, queue is empty. front = -1, rear = -1.
Operation: Enqueue 10
Since queue is empty, set front = 0 and rear = 0. Insert 10 at index 0.
Queue: [10, _, _, _, _]
Step 2: Enqueue 20
Increment rear to 1, insert 20 at index 1.
Queue: [10, 20, _, _, _]
Step 3: Enqueue 30
Increment rear to 2, insert 30 at index 2.
Queue: [10, 20, 30, _, _]
Step 4: Dequeue one element
Remove element at front = 0 (which is 10). Increment front to 1.
Queue logically: [_, 20, 30, _, _]
Step 5: Enqueue 40
Increment rear to 3, insert 40 at index 3.
Queue: [_, 20, 30, 40, _]
Final state: front = 1, rear = 3, queue elements from index 1 to 3 are [20, 30, 40].
Step 1: Enqueue 100
front = 0, rear = 0, queue: [100, _, _, _, _]
Step 2: Enqueue 200
rear = (0 + 1) mod 5 = 1, queue: [100, 200, _, _, _]
Step 3: Enqueue 300
rear = (1 + 1) mod 5 = 2, queue: [100, 200, 300, _, _]
Step 4: Dequeue one element
Remove element at front = 0 (100), front = (0 + 1) mod 5 = 1
Queue logically: [_, 200, 300, _, _]
Step 5: Enqueue 400
rear = (2 + 1) mod 5 = 3, queue: [_, 200, 300, 400, _]
Step 6: Enqueue 500
rear = (3 + 1) mod 5 = 4, queue: [_, 200, 300, 400, 500]
Step 7: Enqueue 600
Now, rear = 4, next rear would be (4 + 1) mod 5 = 0. Since front = 1, the queue is not full yet.
Insert 600 at index 0 (wrap-around), queue: [600, 200, 300, 400, 500]
Final pointers: front = 1, rear = 0
Step 1: Insert (A, 3)
Queue: [(A,3)]
Step 2: Insert (B, 1)
Queue sorted by priority: [(B,1), (A,3)]
Step 3: Insert (C, 2)
Queue sorted: [(B,1), (C,2), (A,3)]
Step 4: Insert (D, 4)
Queue sorted: [(B,1), (C,2), (A,3), (D,4)]
Step 5: Dequeue first element
Remove (B,1) because it has the highest priority (lowest number).
Queue now: [(C,2), (A,3), (D,4)]
Step 6: Dequeue second element
Remove (C,2), next highest priority.
Queue now: [(A,3), (D,4)]
Initial state: front = null, rear = null
Insert 5:
front and rear to this node.Queue: front -> [5] <- rear
Insert 10:
rear.next to new node.rear to new node.Queue: front -> [5] -> [10] <- rear
Insert 15:
rear.next to new node.rear to new node.Queue: front -> [5] -> [10] -> [15] <- rear
Dequeue one element:
front (5).front to front.next (node with 10).front becomes null, set rear to null (not the case here).Queue: front -> [10] -> [15] <- rear
Step 1: Initialize queue with processes in order: P1, P2, P3
Queue: front -> [P1] -> [P2] -> [P3] <- rear
Step 2: First cycle of execution
Queue after first cycle: [P1, P2, P3]
Step 3: Second cycle
Queue after second cycle: [P1, P2, P3]
Step 4: Third cycle
Execution order over 3 cycles: P1 -> P2 -> P3 -> P1 -> P2 -> P3 -> P1 -> P2 -> P3
This demonstrates how queues efficiently manage CPU time-sharing among processes in operating systems.
When to use: Helps recall the fundamental principle of queues during conceptual questions.
When to use: When implementing or solving problems related to circular queues.
When to use: When solving problems that involve priority-based processing.
When to use: Useful during implementation and debugging.
When to use: Prepares for coding and theory questions in competitive exams.
front points to the element to be dequeued, rear points to the last inserted element.rear = index of last element, front = index of first element, capacity = total size of the queue arrayProgress tracking is paywalled — subscribe to mark subtopics as understood and save your streak.
Go to practice →