Queue

A Queue ADT stores arbitrary objects and follows the First-In, First-Out (FIFO) principle.

The first element inserted into the queue is the first one removed.

Queue Discipline (FIFO)

Term Meaning
Front Position where elements are removed
Rear (Back) Position where elements are added

1.1 Abstract Data Type (ADT): Queue

Operation Description
enqueue(x) Insert element x at the rear
dequeue() Remove and return the front element
peek() / front() Return the front element without removing it
is_empty() Check whether the queue is empty
size() Return the number of elements

Exceptions: Attempting the execution of dequeue or front on an empty queue throws an EmptyQueueException

1.2. Object-Oriented Design of a Queue Class

1.2.1. Attributes

items: a container to store queue elements

1.2.2. Methods

1.3. Implementation 1: Queue Using a Python List

1.3.1. Array-Based Queue Implementation

A queue can be efficiently implemented using an array of fixed size N in a circular fashion.

Instead of shifting elements, the array is treated as circular, wrapping around when the end is reached.

Two integer variables are used to track the queue:

Important rule

1
2
Array indices: 0  1  2  3  4  ...  N-1
Queue wraps around using modulo arithmetic

1.3.2. Size Operation

1
2
Algorithm size()
	return (N - f + r) mod N

Explanation:

1.3.3. isEmpty Operation

1
2
Algorithm isEmpty()
    return (f = r)

Explanation:

1.3.4. Enqueue Operation (Full Queue Case)

The enqueue operation inserts an element at position r

After insertion: r <- (r + 1) mod N

1
2
3
4
5
6
Algorithm enqueue(o)
	if size() = N <- 1 then
		throw FullQueueException
	 else  
		Q[r] <- o
		r <- (r + 1) mod N

In this case:

1.3.5. Code Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Queue:
    def __init__(self):
        self.items = []

    def is_empty(self):
        return len(self.items) == 0

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        if self.is_empty():
            raise IndexError("Queue is empty")
        return self.items.pop(0)

    def peek(self):
        if self.is_empty():
            raise IndexError("Queue is empty")
        return self.items[0]

    def size(self):
        return len(self.items)

1.3.6. Example Usage

Operation Return Value Queue (first <- Q <- last)
Q.enqueue(5) [5]
Q.enqueue(3) [5, 3]
len(Q) 2 [5, 3]
Q.dequeue() 5 [3]
Q.is_empty() False [3]
Q.dequeue() 3 []
Q.is_empty() True []
Q.dequeue() “error” []
Q.enqueue(7) [7]
Q.enqueue(9) [7, 9]
Q.first() 7 [7, 9]
Q.enqueue(4) [7, 9, 4]
len(Q) 3 [7, 9, 4]
Q.dequeue() 7 [9, 4]

Examples:

1
2
3
4
5
6
7
8
9
q = Queue()

q.enqueue(5)
q.enqueue(10)
q.enqueue(15)

print(q.dequeue())  # Output: 5
print(q.peek())     # Output: 10
print(q.size())     # Output: 2

Direct applications

Indirect applications

Array-based Queue

Queue Operations

1
2
3
4
5
Algorithm size()
	return (N - f + r) mod N

Algorithm isEmpty()
	return (f = r)
1
2
Operation enqueue throws an exception if the array is full
This exception is implementation-dependent

1.3.7. Time Complexity Analysis (List-Based Queue)

Operation Time Complexity Explanation
enqueue O(1) Append to end of list
dequeue O(n) All elements shift left
peek O(1) Direct index access
is_empty O(1) Length check

Using list.pop(0) is inefficient for large queues.

1.4. Implementation 2: Efficient Queue Using collections.deque

Python provides deque (double-ended queue), optimized for FIFO operations.

1.4.1 Code Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from collections import deque

class Queue:
    def __init__(self):
        self.items = deque()

    def is_empty(self):
        return len(self.items) == 0

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        if self.is_empty():
            raise IndexError("Queue is empty")
        return self.items.popleft()

    def peek(self):
        if self.is_empty():
            raise IndexError("Queue is empty")
        return self.items[0]

    def size(self):
        return len(self.items)

1.4.2. Time Complexity (Deque-Based Queue)

Operation Time Complexity
enqueue O(1)
dequeue O(1)
peek O(1)

1.5 Comparison of Queue Implementations

Implementation Pros Cons
List-based Simple, intuitive Slow dequeue()
Deque-based Fast, scalable Requires import
Circular Queue Memory efficient More complex logic

2. Stack

An Abstract Data Type (ADT) is an abstraction of a data structure. It defines what operations are supported and how the data behaves, without specifying how the data is implemented.

A Stack is a linear data structure that follows the LIFO principle: \(LIFO:Last-In, First-Out\)

This means:

An ADT specifies:

Example: ADT for a Stock Trading System

Data Stored: Buy and sell orders

Supported Operations

1
2
3
order buy(stock, shares, price)
order sell(stock, shares, price)
void cancel(order)

Error Conditions

2.1 Basic Terminologies of Queue

A stack has one accessible end only, called the Top.

Term Meaning
Top Position where elements are added and removed
Bottom The first element added

2.2 Abstract Data Type (ADT)

An Abstract Data Type (ADT) defines the behavior of a stack, independent of implementation.

Operation Description
push(x) Insert element x onto the top
pop() Remove and return the top element
peek() / top() Return the top element without removing it
is_empty() Check if the stack is empty
size() Return the number of elements

2.3. Object-Oriented Design of a Stack Class

2.3.1. Attributes

2.3.2 Methods

2.4. Implementation 1: Stack Using a Python List

2.4.1. Code Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Stack:
    def __init__(self):
        self.items = []

    def is_empty(self):
        return len(self.items) == 0

    def push(self, item):
        self.items.append(item)

    def pop(self):
        if self.is_empty():
            raise IndexError("Stack is empty")
        return self.items.pop()

    def peek(self):
        if self.is_empty():
            raise IndexError("Stack is empty")
        return self.items[-1]

    def size(self):
        return len(self.items)

2.4.2. Example Usage

1
2
3
4
5
6
7
8
9
s = Stack()

s.push(10)
s.push(20)
s.push(30)

print(s.pop())    # Output: 30
print(s.peek())   # Output: 20
print(s.size())   # Output: 2

2.4.3. Time Complexity Analysis (List-Based Stack)

Operation Time Complexity Explanation
push O(1) Append to end of list
pop O(1) Remove from end
peek O(1) Direct index access
is_empty O(1) Length check

Very efficient using Python lists.

What’s happen? When:

2.5. Implementation 2: Bounded Stack (Optional Extension)

Application Description
Function calls Call stack in memory
Undo / Redo Text editors
Expression evaluation Postfix / Prefix
Syntax checking Parentheses matching
DFS Graph traversal

2.5.1 Implement Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class BoundedStack:
    def __init__(self, capacity):
        if capacity <= 0:
            raise ValueError("Capacity must be a positive integer")

        self.capacity = capacity
        self.items = []

    def is_empty(self):
        return len(self.items) == 0

    def is_full(self):
        return len(self.items) == self.capacity

    def push(self, item):
        if self.is_full():
            raise OverflowError("Stack overflow: cannot push onto a full stack")
        self.items.append(item)

    def pop(self):
        if self.is_empty():
            raise IndexError("Stack underflow: cannot pop from an empty stack")
        return self.items.pop()

    def peek(self):
        if self.is_empty():
            raise IndexError("Stack is empty")
        return self.items[-1]

    def size(self):
        return len(self.items)

    def __str__(self):
        return f"Stack(bottom → top): {self.items}"

2.5.2. Using

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
stack = BoundedStack(3)

stack.push(10)
stack.push(20)
stack.push(30)

print(stack)        # Stack(bottom → top): [10, 20, 30]
print(stack.peek()) # 30

# Stack overflow
# stack.push(40)    # OverflowError

print(stack.pop())  # 30
print(stack.pop())  # 20
print(stack.size()) # 1

# Stack underflow
# stack.pop()
# stack.pop()       # IndexError

2.5.3. Time & Space Complexity

Operation Time Complexity
push O(1)
pop O(1)
peek O(1)
is_empty O(1)
is_full O(1)

3. Excercises

3.1. Applying the Queue Data Structure – Service Queue Management System

3.1.1. Problems

In many real-world service systems such as banks, public service centers, hospitals, and customer support offices, service requests are typically processed in a First In – First Out (FIFO) order.

Without a proper queue management mechanism:

Therefore, the problem is to design and implement a queue-based service management system that:

The system must operate in a console-based environment and must not use any database.

3.1.2. System Requirements

The system must support the following functions:

  1. Add a customer to the queue
  2. Serve a customer
  3. View the next customer
  4. Display the entire queue: show all waiting customers in the correct order
  5. System statistics

3.1.3. Data Requirements

Attribute Data Type Description
id int Customer identifier
name string Customer name
service_type string Type of service
arrival_time int or float Arrival time

3.1.4. Technical Requirements

3.1.5 Input / Output Specification

— Input —

Main menu:

1
2
3
4
5
6
7
8
===== SERVICE QUEUE MANAGEMENT =====
1. Add customer to queue
2. Serve next customer
3. View next customer
4. Display queue
5. Show statistics
6. Exit
===================================

Input for adding a customer:

1
2
3
4
Customer ID: 101
Customer Name: Nguyen Van A
Service Type: Payment
Arrival Time: 12.5

— Output —

When a customer is successfully added:

1
Customer added to queue successfully.

When serving a customer:

1
2
3
4
Serving customer:
ID: 101
Name: Nguyen Van A
Service Type: Payment

When viewing the next customer:

1
2
3
4
Next customer in queue:
ID: 102
Name: Tran Thi B
Service Type: Registration

When the queue is empty:

1
The queue is empty. No customer to serve.

When displaying the entire queue:

1
2
3
Current Queue:
1. ID: 102 - Tran Thi B - Registration
2. ID: 103 - Le Van C - Inquiry

When showing statistics:

1
2
3
Total customers served: 5
Customers waiting: 2
Average waiting time: 4.2 minutes

3.2. Applying the Stack Data Structure – Expression Processing System

3.2.1. Problems

In many areas of computer science such as compilers, calculators, and programming language interpreters, expressions (arithmetic or logical) must be processed in a specific order.

However, expressions written in infix notation (e.g. 3 + 4 * 2) are not easy for computers to evaluate directly because operator precedence and parentheses must be handled correctly.

The problem is to design a system that:

The system must be implemented as a console-based program without using any external libraries for expression evaluation.

3.2.2. Requirements

  1. Input an infix expression
  2. Convert infix expression to postfix notation
  3. Display the postfix expression: output the converted postfix expression as a string
  4. Evaluate the postfix expression
  5. Handle invalid expressions
  6. The system must use a Stack to store:
  7. Stack operations required: push, pop, peek, is_empty

3.2.3. Input / Output Specification

—Input—

Main menu:

1
2
3
4
5
6
7
===== STACK EXPRESSION PROCESSOR =====
1. Enter infix expression
2. Convert infix to postfix
3. Display postfix expression
4. Evaluate postfix expression
0. Exit
=====================================

Example input:

1
Enter infix expression: 3 + (4 * 5)

—Output—

After converting infix to postfix:

1
Postfix expression: 3 4 5 * +

After evaluating postfix expression:

1
Evaluation result: 23

If the expression is invalid:

1
Error: Mismatched parentheses.

If division by zero occurs:

1
Error: Division by zero.