← Back to Projects

Case Study · ENSF 338

Campus Navigation System

A Python desktop app that turns a university campus into a live data structure problem. Six custom data structures — AVL tree, min-heap, FIFO queue, circular stack, weighted graph, and HashMap — all implemented from scratch as part of a data structures course.

PythonPySide6AlgorithmsData Structures
6custom data structures
O(1)lookup (benchmarked)
5test modules

Overview

What it does

🗺️

Find the fastest route

Pick any two buildings and Dijkstra's algorithm returns the shortest walking path across the weighted campus graph — instantly, on every query.

📅

Search room bookings

Filter room availability by date and time range. Results come back in chronological order, thanks to an AVL tree's in-order traversal.

Prove O(1) lookup

The fast lookup module doesn't just use a HashMap — it benchmarks it across 7 different sizes and plots the flat curve proving constant time.

🔧

No standard library data structures

The priority queue, AVL tree, FIFO queue, and stack are all hand-written Python classes. This was a deliberate constraint — the point wasn't to use the tools, it was to understand them.

Data Structures

Six structures, each with a job

Each structure was chosen because it was the right fit — not because it was easy.

AVL Tree

Room bookings per room

Self-balancing keeps insert and range-query at O(log n) even as bookings pile up. All four rotation cases hand-written.

Min-Heap

Dijkstra's + service desk

The same heap powers both shortest-path and priority service requests — extract-min in O(log n) without any library.

FIFO Queue

Request pipeline

Every GUI action is queued before processing. A moving front pointer keeps dequeue O(1) amortized — no shifting the list on every pop.

Weighted Graph

Campus map

Buildings are nodes, walkways are weighted edges. An adjacency dict makes neighbour lookup O(1) and keeps Dijkstra's implementation clean.

HashMap

Building / room lookup

Python's built-in dict gives O(1) lookup. A benchmark module proves this empirically across sizes from 100 to 100,000 entries.

Circular Buffer Stack

Path history

Fixed-capacity LIFO stack backed by a circular array. Push and pop wrap with modulo — constant time, no resizing.

Algorithm deep dive

How the shortest path works

The campus is an adjacency dict — each building maps to its neighbours with walking times as edge weights. Dijkstra's explores the cheapest unvisited node at each step (via the min-heap), skips stale entries, and reconstructs the path by tracing backwards through a previous dict once the destination is reached.

campus.py — dijkstra()
def dijkstra(self, start_id, end_id):
    distances = {bid: float('inf') for bid in self.buildings}
    previous  = {bid: None         for bid in self.buildings}
    distances[start_id] = 0

    pq = PriorityQueue()           # custom min-heap
    pq.enqueue((0, start_id))

    while not pq.is_empty():
        curr_dist, curr_id = pq.dequeue()
        if curr_dist > distances[curr_id]:
            continue               # stale entry — skip

        for neighbor_id, weight in self.pathways[curr_id].items():
            dist = curr_dist + weight
            if dist < distances[neighbor_id]:
                distances[neighbor_id] = dist
                previous[neighbor_id]  = curr_id
                pq.enqueue((dist, neighbor_id))

    # Walk backwards through previous[] to reconstruct the path
    path, current = [], end_id
    while current is not None:
        path.append(current)
        current = previous[current]
    path.reverse()
    return path, distances[end_id]
📐

AVL tree — why balance matters

Room bookings are inserted by (date, time) key. Without balancing, a sorted stream of bookings would degrade a plain BST into a linked list — O(n) per query. The AVL tree's rotations keep the height bounded, so range queries stay fast even with thousands of bookings.

📬

The request pipeline

Every user interaction — finding a path, looking up a room, submitting a service request — is wrapped in a typed Request object and pushed into the FIFO queue. A RequestProcessor dequeues and dispatches each one. The GUI never calls the campus logic directly.

Walkthrough

The app in action

Home screen
01

The home screen gives you six entry points. Each one demonstrates a distinct data structure in action — it's as much a learning tool as it is an app.

Campus map with Dijkstra path
02

The map renders the campus as a weighted graph — school buildings in red, other locations in grey, edges labelled with walking times. The selected path (ENG → MAC → TFDL → HNT → HSK, total 14 min) is highlighted above the map after Dijkstra's runs.

Room booking system
03

60 bookings found across all rooms in a date/time range — returned in chronological order from the AVL tree's in-order traversal.

O(1) HashMap lookup benchmark
04

The right chart is the interesting one — lookup time stays flat as the HashMap grows from 100 to 100,000 entries. O(1) empirically confirmed.

Takeaways

What this built

🧠

Algorithms become intuitive

Implementing Dijkstra's on a real graph — not a textbook example — makes the priority queue's role concrete. You stop asking 'why a heap?' and start asking 'what else could you use one for?'

📊

The right structure for the job

Using an AVL tree for time-range queries and a plain HashMap for lookups is an active decision. Building both from scratch means you understand the trade-off, not just the API.

🔬

Benchmarking over claiming

The fast lookup module doesn't say 'HashMaps are O(1)' — it proves it with a chart. That habit of measuring rather than assuming carries directly into production work.