A modular, console-based Data Structures & Algorithms library written entirely in C, built from scratch with pointer-level control, manual memory management (malloc / free), and defensive input validation.
This project emphasizes conceptual clarity, low-level fundamentals, and explicit memory reasoning. It is designed with an educational intent, allowing learners to observe, experiment with, and understand data structures and algorithms step-by-step through an interactive terminal-based interface.
The codebase is structured as a reusable DSA core, with an interactive, console-driven demo layer built on top.
This project includes a GitHub Actions CI pipeline that automatically verifies code correctness and memory safety.
On every push or pull request:
-
A fresh Ubuntu VM is allocated
-
The project is compiled using GCC
-
The complete unit test suite is executed
-
All test binaries are run under Valgrind to check for:
- memory leaks
- invalid reads / writes
- use-after-free errors
- uninitialized memory usage
If any test fails or Valgrind detects a memory error, the CI job fails automatically.
- Singly Linked List (SLL)
- Doubly Linked List (DLL)
- Circular Queue (array-based)
- Stack (array-based / linked-list-based as required)
- Binary Search Tree (BST)
- Threaded Binary Tree (TBT)
- Infix → Postfix conversion
- Postfix expression evaluation
- Linear Search
- Binary Search
- Bubble Sort
- Selection Sort
- Insertion Sort
- Quick sort
- Merge sort
- Breadth-First Search (BFS)
- Depth-First Search (DFS)
Graph traversals are implemented using:
- An adjacency list representation
- An explicit queue for BFS
- An explicit stack for DFS
Both BFS and DFS are implemented iteratively (no recursion).
-Linear Probing -Separate Chaining
Linear Probing uses modulo arithmetic to wrap-around the hash table/array when last index is full, optimizing resources and using the full array. Separate Chaining uses sll API from the 'data_structures' folder
This project includes a Makefile to simplify building across multiple directories.
- GNU Make ≥ 3.81
- GCC (or a compatible C compiler)
makeThis generates a single executable:
dsa(Linux / macOS)dsa.exe(Windows)
make cleangcc -Wall -Wextra -std=c11 -g \
-Isrc/data_structures \
-Isrc/expression_evaluation \
-Isrc/sorting_algorithms_n2 \
-Isrc/advanced_sorting_algorithms \
-Isrc/searching_algorithms \
-Isrc/graph_traversals \
-Isrc/hashing \
src/data_structures/*.c \
src/expression_evaluation/*.c \
src/sorting_algorithms_n2/*.c \
src/advanced_sorting_algorithms/*.c \
src/searching_algorithms/*.c \
src/graph_traversals/*.c \
src/hashing/*.c \
-o dsagcc -Wall -Wextra -std=c11 -g ^
-Isrc/data_structures ^
-Isrc/expression_evaluation ^
-Isrc/sorting_algorithms_n2 ^
-Isrc/advanced_sorting_algorithms ^
-Isrc/searching_algorithms ^
-Isrc/graph_traversals ^
-Isrc/hashing ^
src/data_structures/*.c ^
src/expression_evaluation/*.c ^
src/sorting_algorithms_n2/*.c ^
src/advanced_sorting_algorithms/*.c ^
src/searching_algorithms/*.c ^
src/graph_traversals/*.c ^
src/hashing/*.c ^
-o dsa.exeThis mirrors exactly what the Makefile performs.
- Linear Search: O(n)
- Binary Search: O(log n)
- Bubble Sort: O(n²)
- Selection Sort: O(n²)
- Insertion Sort: O(n²)
- Quick sort: O(n²)
- Merge sort: O(nlogn)
- BFS: O(V+E)
- DFS: O(V+E)
- Graphs are represented using an adjacency list
- BFS uses the circular queue from the
data_structuresmodule - DFS uses an explicit stack from the
expression_evaluationmodule visited[]invariants are strictly enforced- Traversals are iterative (non-recursive)
-
Stack implementation resides in
expression_evaluation -
Infix → Postfix conversion using:
- Operator precedence
- Parentheses handling
-
Postfix evaluation via a stack execution model
This is a classic two-phase algorithm implemented with full control over execution flow and state.
- Parantheses checker with a stack to check order of parantheses, not just number.
The codebase follows strict modular design rules:
- One
.h/.cpair per logical module - No function definitions inside headers
- No duplicate symbols across translation units
- Explicit namespacing via function prefixes
Each directory acts as an independent module, making the system easy to extend, debug, or refactor.
-
staticfor file-local helper functions -
constfor API correctness and pointer safety -
Macro
INPUT_EXIT_SIGNAL(defined insafe_input.h) for:- Consistent exit signaling
- Uniform validation behavior
All user input across the entire application is handled via:
safe_input_int()
Validation is implemented through custom-built helper functions, not ad-hoc checks.
Examples include:
-
Infix expression validation (
validate_infix_expr)- Allowed tokens
- Balanced parentheses
-
Postfix expression validation (
validate_postfix_expr)- Stack depth invariants
-
Numeric range validation for searching, sorting, and graph input
Invalid input:
- Cannot crash the program
- Is rejected, cleaned, and retried safely
If you're curious about the thinking and struggles behind building this — I wrote about it here - https://dev.to/darshan2456/i-refused-to-just-know-what-a-data-structure-is-so-i-built-one-in-c-3dfp
This project is licensed under the MIT License - see the LICENSE file for details.
Darshan Parekh
Aspiring systems engineer and cybersecurity engineer