Both projects centered on a real-world advising scenario for ABC University (ABCU). The Computer Science department needed software that could load a list of courses from a CSV file, validate that every listed prerequisite actually existed in the curriculum, and then give academic advisors two fast operations: print all courses in alphanumeric order and look up the full details of any single course by its course number. The underlying challenge was not just writing code that worked, but choosing the right data structure so that both operations remained efficient even as the course catalog grew.
My first step was to analyze three candidate structures: vector, hash table, and binary search tree. Before writing a single line of implementation code. For each structure I worked out the Big-O cost of insertion, search, and sorted traversal, then compared them against the two operations the advisors actually needed. That analysis made the trade-offs concrete: a vector is simple but requires an O(n log n) sort every time you want an ordered list; a hash table gives O(1) average search but has no natural sort order; a BST gives O(log n) average search and produces a sorted list for free through in-order traversal. Understanding data structures at that theoretical level — not just as APIs to call, but as arrangements of memory with predictable cost profiles, is what made it possible to justify the design choice rather than just guess. Without that foundation, I would have defaulted to whichever structure felt familiar rather than whichever one actually fit the problem.
The trickiest part of the implementation was the file-loading logic. A naive single-pass approach cannot validate prerequisites because a course referenced on line 5 might not be defined until line 47. I worked through the problem on paper first and realized a two-pass strategy was needed: the first pass reads every line, rejects any row with fewer than two fields, and collects all valid course numbers into a set; the second pass then checks each prerequisite against that set before building the Course object and inserting it into the BST. Breaking the problem into two clearly defined responsibilities: collection then validation, made both passes easier to reason about and test independently. Whenever I hit a logic error during testing I went back to that paper design rather than trying to debug by trial and error, which kept the process much more systematic.
How has your work on this project expanded your approach to designing software and developing programs?
Before this course I tended to jump straight to coding once I understood the general shape of a problem. These projects pushed me to do meaningful design work before touching an editor, writing out pseudocode, drawing the BST node relationships, and producing the run-time analysis table first. That upfront investment paid off in a noticeably smoother implementation phase because structural decisions (what the Node struct holds, how the public and private method boundaries are drawn, why Insert delegates to a private addNode helper) were already settled. I now treat the design phase as load-bearing work rather than optional scaffolding, and I reach for asymptotic analysis early whenever a problem involves choosing between competing approaches.
How has your work on this project evolved the way you write programs that are maintainable, readable, and adaptable?
Working through the BST implementation reinforced several habits that I now apply consistently. I keep public interfaces thin and push recursive logic into private helpers, InOrder() simply calls inOrder(root), which means callers never need to know a root pointer exists. Every method has a single, clearly stated responsibility, which made the remove logic (three distinct cases: leaf, one child, two children) much easier to reason about in isolation. I also write comments that explain why a decision was made, not just what the line does, for example, noting that the in-order successor is guaranteed to have at most one child, which is the fact that makes the two-child removal case safe. Those kinds of annotations make the code readable to a future maintainer who does not have the original design session in memory. Finally, separating the file-loading logic, the BST class, and the menu loop into distinct units means any one of them can be swapped out or extended without touching the others, a structure I intend to carry forward into every project I write.