📚 Stacks & Queues Study Guide

Data Structures and Algorithms - CS210

Complete Reference for Abstract Data Types

đŸ”Ĩ Stacks - LIFO (Last In, First Out)

What is a Stack?

A Stack is an Abstract Data Type (ADT) that stores arbitrary objects following the Last-In First-Out (LIFO) principle. Think of it like a spring-loaded plate dispenser - the last plate you put on top is the first one you take off!

Stack Visualization

d (top)
↓
c
↓
b
↓
a (bottom)

LIFO: Element 'd' was inserted last, so it will be removed first

🔑 Key Characteristics:

  • LIFO Ordering: Last element inserted is the first to be removed
  • Restricted Access: Can only access the top element
  • Dynamic Size: Grows and shrinks with push/pop operations

âš™ī¸ Stack Operations

Main Operations

1. push(object) - Insert Element

Adds an element to the top of the stack

void push(E element); // Inserts element at the top // Updates size

2. pop() - Remove and Return Element

Removes and returns the element at the top of the stack

E pop(); // Removes top element // Returns the removed element // Returns null if stack is empty // Decrements size

Auxiliary Operations

3. top() - Peek at Top Element

Returns the top element without removing it

E top(); // Returns top element without removing // Returns null if empty

4. size() - Get Stack Size

Returns the number of elements in the stack

int size(); // Returns number of elements

5. isEmpty() - Check if Empty

Checks whether the stack contains any elements

boolean isEmpty(); // Returns true if stack is empty

Stack Interface in Java

public interface Stack<E> { int size(); boolean isEmpty(); E top(); void push(E element); E pop(); }

📊 Operation Example Walkthrough

Operation Return Value Stack Contents Size
push(5)—(5)1
push(3)—(5, 3)2
size()2(5, 3)2
pop()3(5)1
isEmpty()false(5)1
pop()5()0
isEmpty()true()0
pop()null()0
push(7)—(7)1
push(9)—(7, 9)2
top()9(7, 9)2
push(4)—(7, 9, 4)3
pop()4(7, 9)2

đŸ’ģ Stack Implementation

Array-Based Stack Implementation

Concept

A simple implementation using an array where:

  • Elements are added from left to right
  • Variable t tracks the index of the top element
  • Initially, t = -1 (no elements yet)

Array Stack Structure

a
b
c

t = 2 (pointing to 'c', the top element)

Key Algorithms

size() Algorithm

Algorithm size() return t + 1 // Add 1 because index starts at 0

pop() Algorithm

Algorithm pop() if isEmpty() then return null else t ← t − 1 return S[t + 1] // Decrement top pointer first // Return element that was at top

push(o) Algorithm

Algorithm push(o) if t = S.length − 1 then throw IllegalStateException else t ← t + 1 S[t] ← o // Check if array is full // Increment top pointer // Store element at new top

Java Implementation

public class ArrayStack<E> implements Stack<E> { // Instance variables private E[] S; // Array for storage private int top = -1; // Index of top element // Constructor public ArrayStack(int capacity) { S = (E[]) new Object[capacity]; } public E pop() { if (isEmpty()) return null; E temp = S[top]; S[top] = null; // Help garbage collection top = top - 1; return temp; } }

âš ī¸ Limitations of Array-Based Stack

  • Fixed Size: Maximum size must be defined in advance
  • Space Waste: May allocate more space than needed
  • FullStackException: Throws exception when trying to push to full stack

đŸŽ¯ Applications of Stacks

Direct Applications

🌐 Web Browser History

Browser's back button uses a stack to track visited pages. Most recent page is on top!

â†Šī¸ Undo Operations

Text editors use stacks to implement undo. Each action is pushed; undo pops the last action.

☕ JVM Method Calls

Java Virtual Machine uses a call stack to manage method invocations and returns.

🔄 Expression Evaluation

Evaluating arithmetic expressions and handling operator precedence.

Parentheses Matching Example

Problem: Check if parentheses are balanced

Each opening bracket "(", "{", "[" must pair with matching closing bracket ")", "}", "]"

Expression Valid?
( )(( )){([( )])}✅ Correct
((( )(( )){([( )])})✅ Correct
)(( )){([( )])}❌ Incorrect (starts with closing)
({[ ])}❌ Incorrect (mismatched)
(❌ Incorrect (unclosed)
// Algorithm: Push opening brackets, pop and match closing brackets public static boolean isMatched(String expression) { Stack<Character> buffer = new LinkedStack<>(); for (char c : expression.toCharArray()) { if (opening.indexOf(c) != -1) // Opening delimiter buffer.push(c); else if (closing.indexOf(c) != -1) { // Closing delimiter if (buffer.isEmpty()) return false; // Nothing to match if (closing.indexOf(c) != opening.indexOf(buffer.pop())) return false; // Mismatched } } return buffer.isEmpty(); // All matched? }

Arithmetic Expression Evaluation

Example: 14 - 3 * 2 + 7

Using operator precedence (* before +/-) and left-to-right evaluation:

  1. Evaluate: 3 * 2 = 6
  2. Evaluate: 14 - 6 = 8
  3. Evaluate: 8 + 7 = 15

Result: 15

Using Two Stacks:

  • opStk: Holds operators (+, -, *, /)
  • valStk: Holds operand values

Strategy: Push operators, but first pop and perform higher/equal precedence operations

đŸšļ Queues - FIFO (First In, First Out)

What is a Queue?

A Queue is an Abstract Data Type (ADT) that stores arbitrary objects following the First-In First-Out (FIFO) principle. Think of it like a waiting line - the first person to arrive is the first to be served!

Queue Visualization

a
→
b
→
c
→
d

Front (a) ← → Rear (d)

Element 'a' was inserted first, so it will be removed first

🔑 Key Characteristics:

  • FIFO Ordering: First element inserted is the first to be removed
  • Two-Ended Access: Insert at rear, remove from front
  • Fair Processing: Elements processed in order of arrival

âš™ī¸ Queue Operations

Main Operations

1. enqueue(object) - Insert at Rear

Adds an element to the rear (end) of the queue

void enqueue(E element); // Inserts element at the rear // Updates size

2. dequeue() - Remove from Front

Removes and returns the element at the front of the queue

E dequeue(); // Removes front element // Returns the removed element // Returns null if queue is empty

Auxiliary Operations

3. first() - Peek at Front

Returns the front element without removing it

E first(); // Returns front element without removing // Returns null if empty

4. size() - Get Queue Size

int size(); // Returns number of elements

5. isEmpty() - Check if Empty

boolean isEmpty(); // Returns true if queue is empty

Queue Interface in Java

public interface Queue<E> { int size(); boolean isEmpty(); E first(); void enqueue(E element); E dequeue(); }

📊 Operation Example Walkthrough

Operation Return Value Queue Contents Size
enqueue(5)—(5)1
enqueue(3)—(5, 3)2
dequeue()5(3)1
enqueue(7)—(3, 7)2
dequeue()3(7)1
first()7(7)1
dequeue()7()0
dequeue()null()0
isEmpty()true()0
enqueue(9)—(9)1
enqueue(7)—(9, 7)2
size()2(9, 7)2
enqueue(3)—(9, 7, 3)3
enqueue(5)—(9, 7, 3, 5)4
dequeue()9(7, 3, 5)3

đŸ’ģ Queue Implementation

Array-Based Circular Queue

Concept

Uses an array of size N in a circular fashion:

  • f = index of the front element
  • sz = number of stored elements
  • r = (f + sz) mod N = first empty slot past rear

Normal Configuration

f
r

Wrapped-Around Configuration

r
f

Queue wraps around using modulo arithmetic

Key Algorithms

enqueue(o) Algorithm

Algorithm enqueue(o) if size() = N − 1 then throw IllegalStateException else r ← (f + sz) mod N Q[r] ← o sz ← sz + 1 // Calculate rear position using modulo // Insert at rear // Increment size

dequeue() Algorithm

Algorithm dequeue() if isEmpty() then return null else o ← Q[f] f ← (f + 1) mod N sz ← sz − 1 return o // Get front element // Move front pointer forward (circular) // Decrement size

Java Implementation

public class ArrayQueue<E> implements Queue<E> { private E[] data; private int f = 0; // Front index private int sz = 0; // Current size public ArrayQueue(int capacity) { data = (E[]) new Object[capacity]; } public void enqueue(E e) throws IllegalStateException { if (sz == data.length) throw new IllegalStateException("Queue is full"); int avail = (f + sz) % data.length; data[avail] = e; sz++; } public E dequeue() { if (isEmpty()) return null; E answer = data[f]; data[f] = null; // Help garbage collection f = (f + 1) % data.length; sz--; return answer; } }

Queue Applications

📋 Waiting Lists

Customer service queues, print job queues, task scheduling

đŸ–¨ī¸ Resource Sharing

Printer queues, CPU scheduling, shared resource access

🔄 Round Robin Scheduling

Fair time-sharing systems, process scheduling

🌐 BFS Algorithm

Breadth-First Search in graphs and trees

Round Robin Scheduler

Implement fair resource sharing by repeatedly:

  1. e = Q.dequeue() - Get next task
  2. Service element e - Process the task
  3. Q.enqueue(e) - Return to back of queue

⚡ Performance & Complexity Analysis

Stack Performance

Operation Array Implementation Linked List Implementation
push() O(1) O(1)
pop() O(1) O(1)
top() O(1) O(1)
size() O(1) O(1)
isEmpty() O(1) O(1)
Space O(N) - fixed size O(n) - dynamic

Queue Performance

Operation Array Implementation Linked List Implementation
enqueue() O(1) O(1)
dequeue() O(1) O(1)
first() O(1) O(1)
size() O(1) O(1)
isEmpty() O(1) O(1)
Space O(N) - fixed size O(n) - dynamic

✅ Key Takeaways:

  • All primary operations run in constant time O(1)
  • Array-based: Fixed size, potential space waste, may throw exceptions when full
  • Linked List-based: Dynamic size, more memory overhead per element
  • Space complexity is O(n) where n is number of elements

Stack vs Queue Comparison

Stack (LIFO)

  • Order: Last In, First Out
  • Access: One end (top only)
  • Operations: push, pop, top
  • Use Cases: Undo, recursion, parsing
  • Analogy: Stack of plates

Queue (FIFO)

  • Order: First In, First Out
  • Access: Two ends (front & rear)
  • Operations: enqueue, dequeue, first
  • Use Cases: Scheduling, BFS, buffering
  • Analogy: Waiting line

📝 Study Summary

Essential Concepts to Remember

Stack Essentials:

  • LIFO principle - last in, first out
  • Main operations: push(add), pop(remove), top(peek)
  • All operations are O(1) constant time
  • Array implementation uses index variable for top
  • Used for: undo operations, expression evaluation, recursive algorithms

Queue Essentials:

  • FIFO principle - first in, first out
  • Main operations: enqueue(add to rear), dequeue(remove from front), first(peek)
  • All operations are O(1) constant time
  • Array implementation uses circular buffer with modulo arithmetic
  • Used for: scheduling, BFS, resource sharing, buffering

🎓 Exam Tips

  • Understand the difference between LIFO and FIFO
  • Be able to trace through push/pop and enqueue/dequeue operations
  • Know the time complexity of all operations (O(1))
  • Understand circular queue implementation with modulo
  • Be familiar with practical applications of each structure
  • Practice parentheses matching and expression evaluation
  • Know when to use a stack vs. a queue