Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
5 views12 pages

Stack Queue CircularQueue Detailed2

This document provides a comprehensive overview of stacks, queues, and circular queues, detailing their definitions, characteristics, implementations, and applications. It includes pseudocode, diagrams, complexity analysis, and exercises for practical understanding. The content is structured in a textbook format, making it suitable for educational purposes in data structure concepts.

Uploaded by

ratanu4u
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views12 pages

Stack Queue CircularQueue Detailed2

This document provides a comprehensive overview of stacks, queues, and circular queues, detailing their definitions, characteristics, implementations, and applications. It includes pseudocode, diagrams, complexity analysis, and exercises for practical understanding. The content is structured in a textbook format, making it suitable for educational purposes in data structure concepts.

Uploaded by

ratanu4u
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Stacks, Queues and Circular Queues — Detailed

Theory
Comprehensive textbook-style treatment with definitions, ADTs, implementations, pseudocode,
diagrams, examples, applications, complexity analysis and exercises.

Prepared by: ChatGPT (Assistant)


Date: 21 September 2025
Table of Contents
1. Introduction
2. Stack
2.1 Definition & Characteristics
2.2 Abstract Data Type (ADT) & Operations
2.3 Array-Based Implementation (Pseudocode)
2.4 Linked-List Implementation (Pseudocode)
2.5 Examples & Step-by-step Diagrams
2.6 Applications
2.7 Advantages & Limitations
2.8 Exercises
3. Queue
3.1 Definition & Types
3.2 ADT & Operations
3.3 Array-Based (Linear) Implementation
3.4 Linked-List Implementation
3.5 Examples & Diagrams
3.6 Applications
3.7 Variants (Deque, Priority Queue)
3.8 Exercises
4. Circular Queue
4.1 Motivation & Concept
4.2 ADT & Operations
4.3 Array Implementation with Modulo Arithmetic
4.4 Handling Full vs Empty (Techniques)
4.5 Circular Linked-List Implementation
4.6 Examples & Diagrams
4.7 Applications
4.8 Exercises
5. Comparison & Complexity Summary
6. Conclusion
7. References & Suggested Reading
8. Appendix: Solution Hints to Exercises
1. Introduction
Data structures are organized ways to store, manage and access data efficiently. Three
fundamental linear data structures are the Stack, the Queue and the Circular Queue. These
structures are building blocks for many algorithms and system designs. This chapter provides a
thorough textbook-style exposition for each structure: formal ADT specification, multiple
implementations, step-by-step examples, pseudocode, complexity analysis and real-world
applications.

2. Stack
2.1 Definition & Characteristics
A stack is a linear abstract data type (ADT) that follows the Last-In, First-Out (LIFO) discipline:
the most recently inserted element is the first that can be removed. A stack has a single
accessible end called the top. Typical operations allow pushing to and popping from the top.
2.2 Abstract Data Type (ADT) & Operations
Stack ADT (for elements of type Item): create() : Stack — create and return an empty stack.
isEmpty(S) : boolean — true if S contains no elements. push(S, x) — insert element x on top of
S. pop(S) : Item — remove and return the top element. Precondition: S not empty. peek(S) :
Item — return the top element without removing it. Precondition: S not empty. size(S) : int —
return the number of elements in S. Each operation should specify preconditions and the
expected time complexity. In standard implementations, push, pop and peek are O(1).
2.3 Array-Based Implementation (Fixed-size)
An array-based stack uses an array A[0..N-1] and an integer topIndex. topIndex = -1 indicates
empty stack. Operations (assume 0-based indexing):
// Array-based stack (capacity = N)
create():
topIndex = -1
A = new array[N]

isEmpty():
return topIndex == -1

isFull():
return topIndex == N-1

push(x):
if isFull(): error "stack overflow"
topIndex = topIndex + 1
A[topIndex] = x

pop():
if isEmpty(): error "stack underflow"
x = A[topIndex]
topIndex = topIndex - 1
return x

peek():
if isEmpty(): error "stack is empty"
return A[topIndex]
Time complexity: push/pop/peek — O(1). Space: O(N).
2.4 Linked-List Implementation (Dynamic)
A linked-list based stack uses a singly linked list where insertions and deletions happen at the
head. This yields an unbounded stack limited only by memory.
// Singly-linked list stack
Node { value, next }

create():
top = NULL

isEmpty():
return top == NULL

push(x):
node = new Node(x)
node.next = top
top = node

pop():
if isEmpty(): error "stack underflow"
x = top.value
top = top.next
return x

peek():
if isEmpty(): error "stack is empty"
return top.value
Time complexity: push/pop/peek — O(1). Space per element: O(1) (dynamic).
2.5 Example & Step-by-step Diagram (Array stack)
Suppose capacity N = 6, initially empty (topIndex = -1). Perform operations: push(10), push(20),
push(30), pop(), push(40), peek(). Step-by-step (showing A and topIndex): 1) push(10): A = [10,
-, -, -, -, -], topIndex = 0 2) push(20): A = [10, 20, -, -, -, -], topIndex = 1 3) push(30): A = [10, 20,
30, -, -, -], topIndex = 2 4) pop(): returns 30, A unchanged, topIndex = 1 5) push(40): A = [10, 20,
40, -, -, -], topIndex = 2 6) peek(): returns 40, stack unchanged ASCII diagram (top on the right):
Bottom -> [10 | 20 | 40] <- Top (topIndex = 2)
2.6 Applications of Stacks (Detailed)
1) Function-call management: The call stack stores return addresses, local variables and
parameters for active procedures. Each function call pushes an activation record; return pops it.
2) Expression evaluation and conversion: Stacks are central to algorithms that convert infix
expressions to postfix (Shunting-yard) and to evaluate postfix expressions. Example: convert (3
+ 4) * 5 to postfix -> 3 4 + 5 * using operator stack. 3) Backtracking and DFS: Depth-first search
uses a stack (implicit via recursion or explicit) to explore paths. Backtracking algorithms use
stack to remember choices. 4) Undo mechanisms: Applications that support undo record past
states on a stack so the most recent change is undone first. 5) Syntax parsing: Compilers use
stacks for matching parentheses and parsing nested constructs.
2.7 Advantages & Limitations
Advantages: - Simple and efficient for LIFO semantics. - Constant-time push/pop in standard
implementations. - Works well with recursion and nested structures. Limitations: - Fixed-size
array implementation can overflow; requires resizing to be unbounded. - LIFO order not suitable
for scenarios requiring random access or FIFO behavior.
2.8 Exercises (Hints in Appendix)
1) Implement a stack that returns the minimum element in O(1) time (design and explain). 2)
Prove that reversing a sequence using a stack requires O(n) space and O(n) time. 3) Use stacks
to evaluate the postfix expression: 2 3 1 * + 9 -
3. Queue
3.1 Definition & Types
A queue is a linear ADT that follows the First-In, First-Out (FIFO) discipline: elements are
inserted at the rear (tail) and removed from the front (head). Common variants include: Linear
queue (simple array or linked-list implementation) Circular queue (array with wrapping) Deque
(double-ended queue — insertion/removal at both ends) Priority queue (elements removed
based on priority)
3.2 ADT & Operations
Queue ADT: create() : Queue — create an empty queue. isEmpty(Q) : boolean enqueue(Q, x)
— insert x at rear. dequeue(Q) : Item — remove and return element from front. front(Q) : Item
— peek front element. rear(Q) : Item — peek rear element. size(Q) : int Typical time complexity
for enqueue/dequeue is O(1) for linked-list and circular-array implementations.
3.3 Array-Based Linear Queue (Problem of Waste)
A naive array-based queue uses an array A[0..N-1] with two indices frontIndex and rearIndex
(e.g., frontIndex = 0, rearIndex = -1 initially). After repeated enqueues and dequeues, frontIndex
increases and space at lower indices becomes unused — causing wasted capacity despite there
being free slots. This motivates the circular queue.
// Linear array queue (inefficient reuse)
create():
frontIndex = 0
rearIndex = -1
A = new array[N]

isEmpty():
return frontIndex > rearIndex

enqueue(x):
if rearIndex == N-1: error "queue overflow"
rearIndex = rearIndex + 1
A[rearIndex] = x

dequeue():
if isEmpty(): error "queue underflow"
x = A[frontIndex]
frontIndex = frontIndex + 1
return x
Time complexity: enqueue/dequeue — O(1). But space utilization is poor without wrap-around.
3.4 Linked-List Implementation
A linked-list implementation keeps pointers to head (front) and tail (rear). Enqueue appends a
node at tail; dequeue removes node from head. Both operations are O(1) if tail pointer is
maintained.
// Linked-list queue with head and tail pointers
Node { value, next }

create():
head = NULL
tail = NULL

isEmpty():
return head == NULL
enqueue(x):
node = new Node(x)
node.next = NULL
if tail != NULL:
tail.next = node
tail = node
if head == NULL:
head = node

dequeue():
if isEmpty(): error "queue underflow"
x = head.value
head = head.next
if head == NULL: tail = NULL
return x
Time complexity: enqueue/dequeue — O(1). Space: O(n) dynamic.
3.5 Example & Diagram (Linked-list queue)
Start empty. enqueue(5), enqueue(8), enqueue(12), dequeue(), enqueue(15). Step-wise (values
from head -> tail): 1) enqueue(5): [5] (head=5, tail=5) 2) enqueue(8): [5 -> 8] (head=5, tail=8) 3)
enqueue(12): [5 -> 8 -> 12] (head=5, tail=12) 4) dequeue(): returns 5, now [8 -> 12] (head=8) 5)
enqueue(15): [8 -> 12 -> 15] (tail=15) ASCII diagram (head on the left): Front -> [8] -> [12] -> [15]
<- Rear
3.6 Applications of Queues (Detailed)
1) Scheduling: CPU scheduling algorithms use queues for ready processes (e.g., round-robin
uses queues). 2) IO buffering: Printer spooling, keyboard input handling — queues buffer events
until processed. 3) BFS in graphs: Breadth-first traversal uses a queue to visit vertices in order of
distance. 4) Message passing: Networking stacks and message queues use queue semantics to
ensure order. 5) Producer-Consumer: Synchronization between producing and consuming
threads/processes frequently uses queues.
3.7 Variants
- Deque (Double-ended queue): supports insertion and deletion at both ends. Implementable
with a doubly linked list or circular buffer. - Priority Queue: elements have priorities; highest (or
lowest) priority element removed first. Implementations: binary heap, Fibonacci heap.
3.8 Exercises
1) Show by example the wasted-space problem in linear array queue after alternating
enqueue/dequeue operations. 2) Implement a deque using a circular array and explain index
arithmetic. 3) Using a linked-list queue, show the steps needed to reverse the queue elements
using O(1) extra space (hint: use stack or recursion — discuss trade-offs).
4. Circular Queue
4.1 Motivation & Concept
A circular queue (ring buffer) is an array-based queue in which the array is treated as circular:
index arithmetic wraps around to the beginning when the end is reached. This eliminates wasted
space that occurs in a naive linear array implementation.
4.2 ADT & Operations
CircularQueue ADT matches the Queue ADT (create, isEmpty, enqueue, dequeue, front, rear,
size), but the implementation uses modulo arithmetic to maintain wrap-around behavior. Two
indices are usually maintained: front and rear. Depending on the convention, rear may point to
last element or to the next insertion slot.
4.3 Array Implementation with Modulo Arithmetic (One-slot empty
technique)
// Circular queue with capacity N, using one empty slot to distinguish full vs empty
create():
front = 0
rear = 0
A = new array[N] // We will store at most N-1 elements

isEmpty():
return front == rear

isFull():
return (rear + 1) % N == front

enqueue(x):
if isFull(): error "queue full"
A[rear] = x
rear = (rear + 1) % N

dequeue():
if isEmpty(): error "queue empty"
x = A[front]
front = (front + 1) % N
return x

size():
return (rear - front + N) % N // number of elements
Note: using the 'one-slot empty' convention reduces usable capacity by 1 but simplifies full/empty
checks.
4.4 Technique: Keep explicit count (full capacity usable)
// Circular queue using count variable (capacity N, store up to N elements)
create():
front = 0
rear = 0
count = 0
A = new array[N]

isEmpty():
return count == 0

isFull():
return count == N
enqueue(x):
if isFull(): error "queue full"
A[rear] = x
rear = (rear + 1) % N
count = count + 1

dequeue():
if isEmpty(): error "queue empty"
x = A[front]
front = (front + 1) % N
count = count - 1
return x

size():
return count
This approach uses an extra integer but allows full utilization of the buffer's capacity.
4.5 Circular Linked-List Implementation
A circular queue can also be implemented using a circular singly linked list where tail.next points
to head. In such a design, tail references the last element; tail.next is the head. Enqueue adds
after tail and updates tail; dequeue removes tail.next and updates accordingly. This provides
dynamic capacity and natural circularity.
4.6 Example & Diagrams (Array circular queue, N=6)
Let N = 6, array indices 0..5. We'll illustrate using one-slot-empty convention (max 5 elements).
Start: front = 0, rear = 0, empty. Operations: enqueue(A), enqueue(B), enqueue(C), dequeue(),
enqueue(D), enqueue(E), enqueue(F), attempt enqueue(G) -> should be full when 5 elements
are present. Steps: 1) enqueue(A): stores at A[0], rear=1 => array indices hold [A, -, -, -, -, -],
front=0, rear=1 2) enqueue(B): store at A[1], rear=2 => [A, B, -, -, -, -] 3) enqueue(C): store at
A[2], rear=3 => [A, B, C, -, -, -] 4) dequeue(): removes A[0], front=1 => remaining logical
elements at indices 1..2: [B, C] 5) enqueue(D): store at A[3], rear=4 => [A, B, C, D, -, -] (A still in
memory but logically removed) 6) enqueue(E): store at A[4], rear=5 => [A, B, C, D, E, -] 7)
enqueue(F): store at A[5], rear=0 (wrap) => [A, B, C, D, E, F] Now front=1, rear=0, isFull() since
(rear+1)%6 == front -> (0+1)%6 == 1 True Attempting enqueue(G) would fail with "queue full".
Current logical queue from front to rear: indices 1,2,3,4,5 -> [B, C, D, E, F]
4.7 Applications of Circular Queues
1) Fixed-size buffers: audio/video streaming buffers, network packet buffers (ring buffers). 2)
Operating systems: scheduling (round-robin), IO buffer management. 3) Embedded systems:
hardware FIFOs for serial I/O, sensor data buffering. 4) Real-time systems: predictable memory
usage with constant-time operations.
4.8 Exercises
1) Implement a circular buffer using a fixed array and show by simulation how wrap-around
works after several enqueues/dequeues. 2) Compare memory and performance trade-offs
between a circular array (one-slot empty) and circular array with count variable. 3) Implement a
circular singly linked list queue and discuss how to detect empty/full (if dynamic, full only occurs
on memory exhaustion).
5. Comparison & Complexity Summary
Below is a concise comparison of the discussed structures, focusing on implementation choices,
time complexities and typical use-cases.
- Stack: * Operations: push, pop, peek * Typical time: O(1) for push/pop/peek * Implementations:
array (fixed), dynamic array (resizable), linked list * Use-cases: recursion, expression evaluation,
backtracking - Queue (linear / linked list): * Operations: enqueue, dequeue, front, rear * Typical
time: O(1) * Implementations: linked list, array (circular preferred) * Use-cases: scheduling, BFS,
buffering - Circular Queue (ring buffer): * Operations: enqueue, dequeue, front, rear * Typical
time: O(1) * Implementations: circular array (preferred for fixed buffer), circular linked list
(dynamic) * Use-cases: IO buffers, round-robin scheduling, embedded FIFO
Complexity summary (amortized):
- Array-based stack: push/pop O(1), but resize (if dynamic) may cause occasional O(n)
operations (amortized O(1) if doubling strategy used). - Linked-list stack: push/pop O(1). - Linear
array queue: enqueue/dequeue O(1) but poor space reuse. - Circular array queue:
enqueue/dequeue O(1) and efficient space reuse. - Linked-list queue: enqueue/dequeue O(1)
with additional pointer overhead.
6. Conclusion
Stacks and queues are elementary yet extremely powerful data structures. They encapsulate
simple access patterns (LIFO for stacks, FIFO for queues) that are central to algorithm design
and system architecture. Circular queues address a practical inefficiency in naive queue
implementations by enabling constant-time wrap-around. Understanding multiple
implementations (array vs linked-list, static vs dynamic) and trade-offs (memory usage, time
complexity, simplicity) is essential for applying these structures correctly in software and
systems.
7. References & Suggested Reading
- Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein — Introduction
to Algorithms (for ADTs and algorithmic analysis). - Mark Allen Weiss — Data Structures &
Algorithm Analysis (array and linked-list implementations). - Robert Sedgewick & Kevin Wayne
— Algorithms, 4th Edition (practical implementations and use-cases). - Goodrich, Tamassia, and
Goldwasser — Data Structures & Algorithms in Java (clear ADT and implementation examples).
- Wikipedia entries on Stack, Queue, Circular buffer (good for quick reference).
8. Appendix: Solution Hints to Exercises
Selected hints and brief solutions for exercises: - Stack minimum in O(1): Use an auxiliary stack
that stores (current_min, count) or push previous mins. On push: if x <= current_min, push
current_min onto aux and update; on pop: if popped element equals current_min, pop aux to
restore previous min. - Reversing a queue using a stack: Dequeue all elements and push to
stack; then pop from stack and enqueue back to queue — this yields reversed order (uses O(n)
extra space). - Circular buffer simulation: Walk through index arithmetic modulo N and show
front/rear updates; visualize with array index diagrams.

You might also like