DSA Material

Stack & Queue

Share to

Stack and queue are restricted-access data structures.

Book alignment (Chapters 8 and 9)

The Necaise sequence presents:

Useful distinction from the book:

Choosing the right Python container

Stack operations

push(value)

Insert at the top. With a Python list, this is append at the end.

Stack push operation

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

pop()

Remove and return the top element. This is the inverse of push.

Stack pop operation

def pop(self):
    if not self.items:
        raise IndexError('pop from empty stack')
    return self.items.pop()

peek()

Read the top value without removing it.

def peek(self):
    if not self.items:
        raise IndexError('peek from empty stack')
    return self.items[-1]

Queue operations

enqueue(value)

Insert at the rear. Linked-list queue keeps this in constant time.

Queue enqueue operation

def enqueue(self, value):
    node = Node(value)
    if self.rear is None:
        self.front = self.rear = node
    else:
        self.rear.next = node
        self.rear = node
    self.size += 1

dequeue()

Remove and return from the front. If the queue becomes empty, reset both front and rear.

Queue dequeue operation

def dequeue(self):
    if self.front is None:
        raise IndexError('dequeue from empty queue')

    value = self.front.value
    self.front = self.front.next
    if self.front is None:
        self.rear = None
    self.size -= 1
    return value

peek()

Read the front value without removing it.

def peek(self):
    if self.front is None:
        raise IndexError('peek from empty queue')
    return self.front.value

Full implementation

class Stack:
    def __init__(self):
        self.items = []

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

    def pop(self):
        if not self.items:
            raise IndexError('pop from empty stack')
        return self.items.pop()

    def peek(self):
        if not self.items:
            raise IndexError('peek from empty stack')
        return self.items[-1]

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


class Node:
    def __init__(self, value, next_node=None):
        self.value = value
        self.next = next_node


class Queue:
    def __init__(self):
        self.front = None
        self.rear = None
        self.size = 0

    def enqueue(self, value):
        node = Node(value)
        if self.rear is None:
            self.front = self.rear = node
        else:
            self.rear.next = node
            self.rear = node
        self.size += 1

    def dequeue(self):
        if self.front is None:
            raise IndexError('dequeue from empty queue')

        value = self.front.value
        self.front = self.front.next
        if self.front is None:
            self.rear = None
        self.size -= 1
        return value

    def peek(self):
        if self.front is None:
            raise IndexError('peek from empty queue')
        return self.front.value

    def is_empty(self):
        return self.front is None

Deque-based queue in real projects

from collections import deque

q = deque()
q.append(10)      # enqueue
q.append(20)
print(q.popleft())  # dequeue -> 10

Use deque when you need both speed and simplicity in production code.

Complexity summary

Operation Stack Queue
Insert O(1) push O(1) enqueue
Remove O(1) pop O(1) dequeue
Peek O(1) O(1)

Typical uses

Pattern example: valid parentheses using stack

def is_valid_parentheses(text):
    pairs = {')': '(', ']': '[', '}': '{'}
    stack = []

    for ch in text:
        if ch in pairs.values():
            stack.append(ch)
        elif ch in pairs:
            if not stack or stack.pop() != pairs[ch]:
                return False

    return len(stack) == 0

Typical mistakes