Skip to content

Quick Start

Get started with py-ds-academy in minutes!

Basic Usage

Stack

A Last-In-First-Out (LIFO) data structure:

from py_ds import Stack

# Create a stack
stack = Stack([1, 2, 3])

# Push an item
stack.push(4)

# Pop an item (removes and returns the last item)
item = stack.pop()  # Returns 4

# Peek at the top without removing
top = stack.peek()  # Returns 3

# Check if empty
is_empty = stack.is_empty()  # False

# Get length
length = len(stack)  # 3

Queue

A First-In-First-Out (FIFO) data structure:

from py_ds import Queue

# Create a queue
queue = Queue([1, 2, 3])

# Enqueue an item
queue.enqueue(4)

# Dequeue an item (removes and returns the first item)
item = queue.dequeue()  # Returns 1

# Peek at the front without removing
front = queue.peek()  # Returns 2

# Iterate over items
for item in queue:
    print(item)  # Prints 2, 3, 4

Linked Lists

Dynamic data structures for efficient insertion and deletion:

from py_ds import LinkedList, DoublyLinkedList

# Linked list
sll = LinkedList([1, 2, 3])
sll.append(4)  # O(1) operation
sll.prepend(0)  # O(1) operation
print(list(sll))  # [0, 1, 2, 3, 4]
print(sll)  # HEAD → 0 → 1 → 2 → 3 → 4 → TAIL

# Doubly linked list (supports reverse iteration / more efficient append/prepend)
dll = DoublyLinkedList([1, 2, 3])
dll.append(4)  # O(1) operation
dll.prepend(0)  # O(1) operation
print(list(dll))  # [0, 1, 2, 3, 4]
print(dll)  # HEAD ⇆ 0 ⇆ 1 ⇆ 2 ⇆ 3 ⇆ 4 ⇆ TAIL

# Reverse iteration (doubly linked only)
for item in dll.reverse_iter():
    print(item)  # Prints 4, 3, 2, 1, 0

Heaps

Priority queue implementations:

from py_ds import MinHeap, MaxHeap

# Min heap (smallest element at top)
min_heap = MinHeap([3, 1, 4, 1, 5])
min_heap.push(2)
smallest = min_heap.pop()  # Returns 1
print(min_heap.peek())  # Returns 1 (next smallest)

# Max heap (largest element at top)
max_heap = MaxHeap([3, 1, 4, 1, 5])
max_heap.push(10)
largest = max_heap.pop()  # Returns 10
print(max_heap.peek())  # Returns 5 (next largest)

Binary Search Tree

Efficient searching and sorting:

from py_ds import BinarySearchTree

# Create a BST
bst = BinarySearchTree([5, 3, 7, 2, 4, 6, 8])

# Print the tree structure (visual representation)
print(bst)
# Output:
#      ┌── 8
#  ┌── 7
#  │   └── 6
#  5
#  │   ┌── 4
#  └── 3
#      └── 2

# Search for an element
if 4 in bst:
    print("Found!")

# Traverse in different orders
print(list(bst.inorder()))    # [2, 3, 4, 5, 6, 7, 8]
print(list(bst.preorder()))   # [5, 3, 2, 4, 7, 6, 8]
print(list(bst.postorder()))  # [2, 4, 3, 6, 8, 7, 5]
print(list(bst.level_order()))  # [5, 3, 7, 2, 4, 6, 8]

# Find min and max
print(bst.min())  # 2
print(bst.max())  # 8

# Remove an element
bst.remove(3)

AVL Tree

Self-balancing binary search tree:

from py_ds import AVLTree

# Create an AVL tree (automatically balances)
avl = AVLTree([1, 2, 3, 4, 5, 6, 7])

# Print the tree structure (visual representation)
print(avl)
# Output:
#      ┌── 7
#  ┌── 6
#  │   └── 5
#  4
#  │   ┌── 3
#  └── 2
#      └── 1

# All BST operations work, with guaranteed O(log n) performance
avl.insert(8)
avl.remove(4)

# Check if balanced
print(avl.height)  # Tree height is kept minimal

Common Patterns

Using Stacks for Expression Evaluation

def evaluate_postfix(expression):
    stack = Stack()
    for token in expression.split():
        if token.isdigit():
            stack.push(int(token))
        else:
            b = stack.pop()
            a = stack.pop()
            if token == '+':
                stack.push(a + b)
            elif token == '-':
                stack.push(a - b)
            elif token == '*':
                stack.push(a * b)
            elif token == '/':
                stack.push(a // b)
    return stack.pop()

result = evaluate_postfix("3 4 + 2 *")  # (3+4)*2 = 14

Using Queues for BFS

def bfs_level_order(tree):
    if not tree.root:
        return []

    queue = Queue([tree.root])
    result = []

    while not queue.is_empty():
        node = queue.dequeue()
        result.append(node.value)

        if node.left:
            queue.enqueue(node.left)
        if node.right:
            queue.enqueue(node.right)

    return result

Using Heaps for Top-K Elements

def find_top_k(items, k):
    min_heap = MinHeap()

    for item in items:
        if len(min_heap) < k:
            min_heap.push(item)
        elif item > min_heap.peek():
            min_heap.pop()
            min_heap.push(item)

    return sorted(min_heap.to_list(), reverse=True)

top_3 = find_top_k([1, 5, 3, 9, 2, 7, 4, 8], 3)  # [9, 8, 7]

Type Hints

All data structures support full type hints:

from typing import List
from py_ds import Stack

def process_stack(items: List[int]) -> Stack[int]:
    stack = Stack(items)
    return stack

Error Handling

Data structures raise appropriate exceptions:

from py_ds import Stack, Queue

stack = Stack()
try:
    stack.pop()  # Raises IndexError
except IndexError:
    print("Stack is empty!")

queue = Queue()
try:
    queue.dequeue()  # Raises IndexError
except IndexError:
    print("Queue is empty!")

Next Steps