Abstract Data Types Using OOP

ADT Queue

A queue implemented with classes and objects — a fair, orderly line where the first item in is the first item out (FIFO). Master the two pointers (front and rear), the enqueue/dequeue pair, and the circular trick (MOD wrap-around) that stops a queue from wasting space. Powers print spoolers, CPU scheduling and breadth-first search.

27.1 Introduction: The Queue ADT

A queue is a linear Abstract Data Type that holds items in the order they arrive and releases them in that same order. It follows the First-In, First-Out (FIFO) rule: the item that has waited longest is served next. Think of students lining up at the canteen — Musarrat joined first, so Musarrat is served first; anyone arriving later joins the back.

Because a queue is an abstract type, the syllabus does not care how you store it underneath — only that it behaves as FIFO. The two common implementations are a 1D array with two pointers (covered here) and a linked list.

TermMeaning
QueueA FIFO ADT: items leave in the same order they entered.
FIFOFirst-In, First-Out — the defining behaviour of a queue.
Front pointerIndex of the next item to be removed (the head of the line).
Rear pointerIndex of the last item added (the back of the line).
EnqueueAdd an item at the rear (the "insert" operation).
DequeueRemove and return the front item (the "delete" operation).
Peek / FrontRead the front item without removing it.
OverflowTrying to enqueue when the queue is already full.
Circular queueA queue whose last index wraps back to index 0 to reuse freed slots.

27.2 How a Queue Works (FIFO + Two Pointers)

Why are we doing this?
  • A stack needed one pointer; a queue needs two — one for each end of the line.
  • Picturing how front and rear move is the whole game.
  • Get the picture right and the code writes itself.

A queue grows at the rear and shrinks at the front. Both pointers only ever move forward (to the right) in a basic linear queue. Watch them as three items are enqueued and one is dequeued:

Tracing enqueue(A), enqueue(B), enqueue(C) then dequeue() — watch front and rear

Empty

·
·
·
·

front=-1 rear=-1

enqueue(A), (B), (C)

A
B
C
·
↑front
↑rear

front=0 rear=2

dequeue() → A

·
B
C
·
↑front
↑rear

front=1 rear=2

enqueue moves rear forward; dequeue moves front forward. The two pointers chase each other to the right. A entered first, so A leaves first — that is FIFO.

Key rule
  • An empty queue has front = -1 and rear = -1.
  • The front pointer marks the next item to be removed; the rear pointer marks the last item added.
  • Enqueue advances rear; dequeue advances front.
Exam tip:
  • When a question gives you a queue diagram and asks you to “describe how two items are added”, name the rear pointer, say it advances, and say the new item is stored at the new rear position.
  • Spell out each pointer movement — examiners award a mark per correct pointer step.

27.3 The Linear Array Queue Class

Why are we doing this?
  • Before the clever circular version, you must be fluent with the plain linear queue.
  • Every Paper 4 queue answer is built from these same five methods.

This class stores items in a fixed list and keeps two integer pointers. A count is also kept so “empty” and “full” are trivial to test.

class Queue:
    def __init__(self, size=10):
        self.items = [None] * size   # fixed-size storage
        self.maxSize = size          # capacity
        self.front = -1              # next item to remove
        self.rear  = -1              # last item added

    def isEmpty(self):
        return self.front == -1

    def isFull(self):
        return self.rear == self.maxSize - 1

    def enqueue(self, item):
        if self.isFull():
            return "Queue Overflow"
        if self.front == -1:         # first item
            self.front = 0
        self.rear += 1               # advance rear, then store
        self.items[self.rear] = item

    def dequeue(self):
        if self.isEmpty():
            return "Queue Underflow"
        item = self.items[self.front]
        self.items[self.front] = None
        if self.front == self.rear:  # last item just left
            self.front = self.rear = -1
        else:
            self.front += 1          # advance front
        return item

    def peek(self):
        if self.isEmpty():
            return "Queue is empty"
        return self.items[self.front]
Line-by-line explanation
  • __init__: A fixed list of empty slots, the capacity, and both pointers set to -1 to mean “empty”.
  • isEmpty: True when front = -1 — the single source of truth for “empty”.
  • isFull: True when rear has reached the last index (maxSize - 1). Note: in a linear queue this can report “full” even when slots at the start are free — the problem we fix with a circular queue.
  • enqueue: Reject if full. If this is the first item, set front = 0. Advance rear, then store.
  • dequeue: Reject if empty. Read the front item, blank the slot. If that was the only item, reset both pointers to -1; otherwise advance front.
  • peek: Return the front item without moving any pointer.
Exam tip:
  • In Cambridge pseudocode the empty test is usually IF FrontPointer = -1 and the full test is IF Length = MaxLength (using a length counter) or IF RearPointer = MaxLength - 1 (linear).
  • State which convention you are using and stay consistent.

27.4 Enqueue & Dequeue, Step by Step

Why are we doing this?
  • Examiner focus: Paper 4 routinely gives you a half-written Enqueue or Dequeue routine and asks you to complete it, or to write one from scratch.
  • The marks come from: the correct full/empty guard, advancing the right pointer, and storing/returning in the right order.

Enqueue (insert at the rear) — Cambridge pseudocode:

PROCEDURE Enqueue(NewItem)
    IF Length = MaxLength THEN
        OUTPUT "Queue full"
    ELSE
        Rear <- Rear + 1
        IF Rear > MaxLength - 1 THEN   // wrap (circular)
            Rear <- 0
        ENDIF
        Queue[Rear] <- NewItem
        IF Front = -1 THEN
            Front <- 0
        ENDIF
        Length <- Length + 1
    ENDIF
ENDPROCEDURE

Dequeue (remove from the front) — Cambridge pseudocode:

FUNCTION Dequeue() RETURNS STRING
    IF Length = 0 THEN
        RETURN ""                 // empty / null
    ENDIF
    ThisItem <- Queue[Front]
    Front <- Front + 1
    IF Front > MaxLength - 1 THEN // wrap (circular)
        Front <- 0
    ENDIF
    Length <- Length - 1
    RETURN ThisItem
ENDFUNCTION
Task — Worked Example — Trace enqueue/dequeue
Trace. A linear queue of size 4 starts empty (front=-1, rear=-1). Show the array, front and rear after: enqueue(7), enqueue(2), dequeue(), enqueue(9).
Hint:
  • enqueue(7): front -1→0, rear -1→0, store 7.
  • enqueue(2): rear 0→1, store 2.
  • dequeue(): read 7, front 0→1.
  • enqueue(9): rear 1→2, store 9.
Your Turn — Your Turn — Trace enqueue/dequeue
Trace. A linear queue of size 4 starts empty (front=-1, rear=-1). Show the array, front and rear after: enqueue(5), enqueue(8), enqueue(1), dequeue().
Hint:
  • First enqueue sets both pointers to 0; dequeue reads front then advances it.
Key rule
  • Enqueue: guard → advance rear (wrap) → store → maybe set front → length+1.
  • Dequeue: guard → read front → advance front (wrap) → length-1 → return.
  • The order of read/store versus pointer-move is what examiners check most.

27.5 The Wasted-Space Problem (Drift)

Why are we doing this?
  • A linear queue has a famous flaw.
  • Understanding why it wastes space is exactly the reasoning that motivates the circular queue in the next section — and it is a popular short-answer question.

In a linear queue both pointers march to the right and never come back. After several dequeues the slots at the start are empty but unusable — rear can hit the end of the array and report “full” even though half the queue is free. We call this pointer drift or wasted space.

Linear queue drift: rear reached the last index, so no more can be added — even though two slots stand empty.

free
free
D
E
F
··↑front=2·↑rear=4 (end!)

isFull() says TRUE, but slots 0 & 1 are free. Wasted space!

The circular queue fixes exactly this — pointers wrap back to index 0 so freed slots are reused.

27.6 The Circular Queue (Wrap-Around)

Why are we doing this?
  • Examiner focus: The circular queue is the version Cambridge examines most.
  • A real Paper 3 question states plainly: “the queue is circular so that locations can be reused.”
  • You must be able to trace the wrap-around and write the MOD step.

A circular queue treats the array as a ring: when a pointer would step past the last index, it wraps back to index 0 using modulo arithmetic (pointer + 1) MOD maxSize. Freed slots at the start are reused, so no space is wasted. A count variable tells full from empty (both can have front == rear).

class CircularQueue:
    def __init__(self, size=10):
        self.queue = [None] * size
        self.maxSize = size
        self.head = 0       # front
        self.tail = 0       # rear (next free slot)
        self.count = 0      # items currently stored

    def is_full(self):
        return self.count == self.maxSize

    def is_empty(self):
        return self.count == 0

    def enqueue(self, item):
        if self.is_full():
            return "Queue is full"
        self.queue[self.tail] = item
        self.tail = (self.tail + 1) % self.maxSize   # wrap-around
        self.count += 1

    def dequeue(self):
        if self.is_empty():
            return "Queue is empty"
        item = self.queue[self.head]
        self.queue[self.head] = None
        self.head = (self.head + 1) % self.maxSize   # wrap-around
        self.count -= 1
        return item
Why count instead of comparing head and rear?
  • In a circular queue, head == tail happens in two different situations: when the queue is completely empty and when it is completely full.
  • The pointers alone cannot tell these apart.
  • Keeping a count of stored items removes the ambiguity: count == 0 means empty, count == maxSize means full.
Task — Worked Example — Trace the wrap
Trace the wrap. A circular queue size 4 (head=0, tail=0, count=0). Do: enqueue(A), enqueue(B), enqueue(C), dequeue(), dequeue(), enqueue(D), enqueue(E).
Hint:
  • enq A,B,C: tail 0→1→2→3, count=3.
  • deq, deq: removes A then B, head 0→1→2, count=1.
  • enqueue(D): store at tail=3, tail→(3+1)%4=0, count=2.
  • enqueue(E): store at tail=0 (wrapped!), tail→1, count=3.
Your Turn — Your Turn — Trace the wrap
Trace the wrap. A circular queue size 4 (head=0, tail=0, count=0). Do: enqueue(P), enqueue(Q), dequeue(), enqueue(R), enqueue(S), enqueue(T).
Hint:
  • tail moves with (tail+1) MOD 4; watch it wrap from 3 to 0.
Exam tip:
  • When asked to “describe how two items are added to a circular queue”, mention: advance the rear/tail pointer, wrap with MOD if it passes the last index, store the item, and increase the count.
  • A missing MOD step or a missing count update each loses a mark.

27.7 Queues in Action — Applications

Why are we doing this?
  • Queues underpin print spoolers, CPU scheduling and breadth-first search.
  • The practical tasks below are the kind of “use a queue to…” problems that appear in Paper 4.
Task — Worked Example — Reverse a queue using a stack
Reverse a queue. Push every dequeued item onto a stack, then pop them back to enqueue — the order flips.
Hint:
  • Dequeue all items, pushing each onto a stack. Then pop each from the stack and enqueue it. LIFO reverses the order.
Your Turn — Your Turn — Predict the reversed dequeue
Predict. Tarnima's queue holds [10, 20, 30] (front first). She runs reverse_queue. What is dequeued first afterwards?
Hint:
  • The last item pushed to the stack is popped first.
Task — Worked Example — Bank / ticket-counter simulation
Serve customers in order. Customers join the rear; the teller serves from the front.
Hint:
  • Enqueue each name; while not empty, print “Serving:” and dequeue.
Your Turn — Your Turn — Extend the bank simulation
Extend the simulation so that, before serving, it prints how many customers are waiting. If the queue is empty it must print "No customers in queue" and stop.
Hint:
  • A count attribute or len(self.queue) gives the number waiting.
Task — Worked Example — Split a file into two queues
Sort numbers into two queues. Read numbers.txt; even numbers go to one queue, odd to another.
Hint:
  • if n % 2 == 0: evenQ.enqueue(n) else: oddQ.enqueue(n).
Your Turn — Your Turn — Split a file into three queues
Naib needs the file split into three queues: numbers below 10, numbers 10-99, and numbers 100 or more. Write the classifying if / elif / else.
Hint:
  • Test the smallest range first, then the middle, then the catch-all.

27.8 Full Exam-Style Question

Paper 4 / Paper 3 · circular queue · ~16 marks
  • A booking program at moshikur.com stores up to 50 ticket records in a circular queue.
  • It uses a 1D array TicketQueue[0:49], an integer FrontPointer, an integer RearPointer and an integer Length.

(a) [2] State the values FrontPointer, RearPointer and Length should hold when the queue is empty, and explain why a circular queue needs a Length (or count) variable. (AO1)

(b) [5] Complete the pseudocode procedure Enqueue(NewID), which adds an ID if there is room, wraps the rear pointer when needed, otherwise outputs "Queue full". (AO2)

PROCEDURE Enqueue(NewID : INTEGER)
    IF Length = .................... THEN
        OUTPUT "Queue full"
    ELSE
        RearPointer <- (RearPointer + 1) ....................
        TicketQueue[RearPointer] <- ....................
        IF FrontPointer = -1 THEN
            FrontPointer <- ....................
        ENDIF
        Length <- ....................
    ENDIF
ENDPROCEDURE

(c) [6] Write the function Dequeue() that returns the front ID, wraps the front pointer, updates Length, or returns -1 if the queue is empty. (AO3)

(d) [3] The queue (size 5) currently holds, from front to rear, IDs at indices 3, 4, 0 with FrontPointer = 3, RearPointer = 0, Length = 3. Show the pointer values after: Enqueue(77), Dequeue(). (AO2)

Lab Task — Show mark scheme
Reveal the mark scheme for parts (a)–(d).
Hint:
  • (a) FrontPointer = -1, RearPointer = -1, Length = 0; Length needed because Front = Rear is ambiguous (empty or full).
  • (b) IF Length = 50; RearPointer <- (RearPointer + 1) MOD 50; TicketQueue[RearPointer] <- NewID; FrontPointer <- 0; Length <- Length + 1.
  • (c) IF Length = 0 RETURN -1; ThisID <- TicketQueue[FrontPointer]; FrontPointer <- (FrontPointer + 1) MOD 50; Length <- Length - 1; RETURN ThisID.
  • (d) Enqueue(77): rear (0+1)%5=1, Length=4; Dequeue: front (3+1)%5=4, Length=3. Final: Front=4, Rear=1, Length=3.

Key Points Summary

A queue is a FIFO ADT — items leave in the same order they entered. The first item enqueued is the first dequeued.
A queue keeps two pointers: front (next item to remove) and rear (last item added). Enqueue advances rear; dequeue advances front.
When the queue is empty, front = -1 and rear = -1. front = -1 is the single source of truth for isEmpty().
Enqueue: guard (full check) → if first item set front = 0 → advance rear → store at rear. In a circular queue, advance with (rear + 1) MOD maxSize.
Dequeue: guard (empty check) → read front item → blank slot → if last item reset both to -1, else advance front. In a circular queue, advance with (front + 1) MOD maxSize.
The linear queue has a drift problem: both pointers march right and never come back. rear can hit the end and report "full" even though slots at the start are free — wasted space.
A circular queue treats the array as a ring: (pointer + 1) MOD maxSize wraps a pointer back to index 0 when it would pass the last index. Freed slots are reused — no space wasted.
In a circular queue, front == rear happens in two situations: completely empty and completely full. The pointers alone cannot tell them apart.
A count (or Length) variable removes the ambiguity: count == 0 means empty, count == maxSize means full. This is why a circular queue needs a count.
peek() returns the front item without moving any pointer — look before you leap.
In Cambridge pseudocode: IF Length = MaxLength for full, IF Length = 0 (or FrontPointer = -1) for empty. Stay consistent with whichever convention the question uses.
Enqueue order: guard → advance rear (wrap) → store → maybe set front → length+1. Dequeue order: guard → read front → advance front (wrap) → length-1 → return.
The order of read/store versus pointer-move is what examiners check most — advance rear before storing; read front before advancing front.
Queues power print spoolers (jobs printed in order sent), CPU scheduling, and breadth-first search. Undo, bracket matching, and the call stack are stacks (LIFO), not queues.
A queue can be reversed using a stack: dequeue all and push onto a stack, then pop each back and enqueue. LIFO reverses the order.
When tracing a circular queue, always track head, tail, AND count — and watch for the wrap (tail going from the last index back to 0).

27.9 Practice Tasks

Fifteen exam-style tasks. Click Hint for bullet-point guidance, then Help to reveal a worked Python/pseudocode solution.

1Practice Task — Declare a Queue class [4 marks]
Write a Queue class with a constructor that takes a size, creates a fixed-size list, stores maxSize, and sets front = -1 and rear = -1.
2Practice Task — isEmpty and isFull (linear) [3 marks]
Write isEmpty() and isFull() methods for the linear Queue class.
3Practice Task — enqueue method (linear) [5 marks]
Write the enqueue(self, item) method: check full, set front=0 if first item, advance rear, store.
4Practice Task — dequeue method (linear) [5 marks]
Write the dequeue(self) method: check empty, read front, blank slot, reset if last item else advance front, return item.
5Practice Task — peek method [2 marks]
Write the peek(self) method that returns the front item without moving any pointer, or a message if empty.
6Practice Task — Trace linear enqueue/dequeue [3 marks]
A linear queue size 4 starts empty. After enqueue(10), enqueue(20), enqueue(30), dequeue(), what is front and rear?
7Practice Task — Pseudocode Enqueue (circular) [5 marks]
Write Cambridge pseudocode for PROCEDURE Enqueue(NewItem) using Length, with MOD wrap-around, full check, and first-item front set.
8Practice Task — Pseudocode Dequeue (circular) [6 marks]
Write Cambridge pseudocode for FUNCTION Dequeue() RETURNS INTEGER using Length, with MOD wrap-around, empty check returning -1.
9Practice Task — Why count in a circular queue? [2 marks]
Explain why a circular queue needs a count (Length) variable.
10Practice Task — CircularQueue class [6 marks]
Write a CircularQueue class with __init__ (queue, maxSize, head=0, tail=0, count=0), is_full, is_empty, enqueue (with MOD wrap), dequeue (with MOD wrap).
11Practice Task — Trace circular wrap [4 marks]
Circular queue size 4, head=0, tail=0, count=0. After enqueue(A), enqueue(B), enqueue(C), dequeue(), dequeue(), enqueue(D), enqueue(E), what is the final state?
12Practice Task — Reverse a queue with a stack [4 marks]
Write a function reverse_queue(q) that reverses a queue using a stack (list).
13Practice Task — Bank simulation [5 marks]
Write a function serve_all(q) that serves customers in FIFO order, printing "Serving:" + name. If empty, print "No customers in queue" and stop.
14Practice Task — Split file into even/odd queues [5 marks]
Write a function split(filename, evenQ, oddQ) that reads numbers from a file and enqueues evens to evenQ, odds to oddQ.
15Practice Task — Full exam-style: ticket circular queue [16 marks]
A circular queue holds 50 ticket IDs in TicketQueue[0:49] with FrontPointer, RearPointer, Length. (a) State values when empty + why Length [2]. (b) Pseudocode Enqueue(NewID) with MOD wrap [5]. (c) Pseudocode Dequeue() returning -1 if empty [6]. (d) Size 5 holds IDs at 3,4,0 (Front=3, Rear=0, Length=3). Show pointers after Enqueue(77), Dequeue() [3].

Question Bank

Answer all questions, then press Submit Quiz to see your score.

0/12 answered

Question 1Multiple Choice

A queue follows which rule?

Question 2Multiple Choice

Which two pointers does a queue keep?

Question 3True / False

When the queue is empty, front = -1 and rear = -1.

Question 4Multiple Choice

Enqueue advances which pointer?

Question 5Multiple Choice

What is the drift problem in a linear queue?

Question 6Multiple Choice

How does a circular queue fix the drift problem?

Question 7Multiple Choice

Why does a circular queue use a count variable?

Question 8True / False

In a circular queue, (pointer + 1) MOD maxSize wraps the pointer back to 0.

Question 9Multiple Choice

What does peek() do in a queue?

Question 10Multiple Choice

Trace: linear queue size 4. After enqueue(7), enqueue(2), dequeue(), enqueue(9), what is front and rear?

Question 11Multiple Choice

Circular queue size 4, head=0, tail=0, count=0. After enqueue(A), enqueue(B), enqueue(C), dequeue(), dequeue(), enqueue(D), enqueue(E), where is E stored?

Question 12Multiple Choice

Which is a real-world use of a queue?

Answer all 12 questions to enable submission.