Project 3: Dynamic Data Structures Library

Implement core linear data structures with strict invariants and memory discipline.

Quick Reference

Attribute Value
Difficulty Intermediate
Time Estimate 2 weeks
Language C
Prerequisites Pointers, malloc/free
Key Topics stacks, queues, lists, invariants

1. Learning Objectives

By completing this project, you will:

  1. Implement stacks, queues, deques, and linked lists from first principles.
  2. Define and enforce invariants for each structure.
  3. Handle allocation failures without corrupting state.
  4. Build a reusable API with tests.

2. Theoretical Foundation

2.1 Core Concepts

  • Linear structures: State changes are local and must preserve links.
  • Invariants: Head/tail consistency and size correctness.
  • Ownership: Clear rules for allocation and freeing.

2.2 Why This Matters

TAOCP uses these structures as building blocks. A single pointer bug corrupts every dependent algorithm.

2.3 Historical Context / Background

These structures predate modern languages and remain the backbone of low-level systems code.

2.4 Common Misconceptions

  • “Size can be recomputed later”: It should be tracked correctly.
  • “NULL checks are enough”: You must also ensure correct link topology.

3. Project Specification

3.1 What You Will Build

A C library implementing stack, queue, deque, singly linked list, and doubly linked list, with iterator support.

3.2 Functional Requirements

  1. Stack: push, pop, peek, size.
  2. Queue: enqueue, dequeue, front, size.
  3. List: insert, remove, find, iterate.
  4. Deque: push/pop both ends.

3.3 Non-Functional Requirements

  • Reliability: All operations leave structure valid.
  • Performance: O(1) for head/tail operations.
  • Usability: Clear error codes and docs.

3.4 Example Usage / Output

stack_push(10)
stack_pop() -> 10
queue_enqueue(5)
queue_dequeue() -> 5

3.5 Real World Outcome

You will have a small library that can be reused in later TAOCP projects without reimplementation.


4. Solution Architecture

4.1 High-Level Design

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│ public API   │────▶│ structure impl│────▶│ tests        │
└──────────────┘     └──────────────┘     └──────────────┘

4.2 Key Components

Component Responsibility Key Decisions
Stack LIFO behavior Array vs list
Queue/Deque FIFO and double-ended Tail pointer
List Insert/remove Singly vs doubly

4.3 Data Structures

typedef struct Node {
    int value;
    struct Node *next;
    struct Node *prev;
} Node;

4.4 Algorithm Overview

Key Algorithm: List insert

  1. Allocate node.
  2. Fix next/prev pointers.
  3. Update head/tail and size.

Complexity Analysis:

  • Time: O(1) for head/tail ops, O(n) for search.
  • Space: O(n) for n elements.

5. Implementation Guide

5.1 Development Environment Setup

gcc --version

5.2 Project Structure

project-root/
├── include/ds.h
├── src/ds.c
├── tests/
└── Makefile

5.3 The Core Question You’re Answering

“Which invariants must always be true after any operation?”

5.4 Concepts You Must Understand First

  1. Pointer ownership and freeing.
  2. Sentinel nodes vs NULL end markers.
  3. Error propagation in C.

5.5 Questions to Guide Your Design

  1. How do you detect and handle allocation failure?
  2. What is the invariant for head/tail pointers?
  3. How do you ensure size stays correct?

5.6 Thinking Exercise

Write assertions that validate a doubly linked list after each mutation.

5.7 The Interview Questions They’ll Ask

  1. What invariants define a correct linked list?
  2. Why might a deque be more efficient than a list?
  3. How do you avoid memory leaks on error paths?

5.8 Hints in Layers

Hint 1: Implement stack using a dynamic array first. Hint 2: Add queue with head/tail pointers. Hint 3: Add doubly linked list with prev pointers.

5.9 Books That Will Help

Topic Book Chapter
Lists TAOCP Vol 1 Ch. 2
C memory Effective C memory chapters

5.10 Implementation Phases

Phase 1: Foundation (3-4 days)

  • Stack and queue implementations.

Phase 2: Core Functionality (4-5 days)

  • Deque and linked lists.

Phase 3: Polish & Edge Cases (2-3 days)

  • Iterators, tests, and invariants.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Stack backing array vs list array cache-friendly
List type singly vs doubly doubly easier removal

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Basic ops Correct behavior push/pop order
Edge cases Empty structures pop on empty
Invariants Structure validity link checks

6.2 Critical Test Cases

  1. Push then pop yields original value.
  2. Queue preserves FIFO order.
  3. List removal preserves connectivity.

6.3 Test Data

Small sequences of integers

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Lost node Leak Track ownership
Broken links Crash Validate pointers
Wrong size Incorrect API behavior Update size on all paths

7.2 Debugging Strategies

  • Add invariant checks in debug builds.
  • Use valgrind for leaks.

7.3 Performance Traps

Repeated scans for tail access; keep tail pointer.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add list iterator API.

8.2 Intermediate Extensions

  • Add freelist allocator for nodes.

8.3 Advanced Extensions

  • Add thread-safe variants.

9. Real-World Connections

  • OS schedulers, buffers, and queues rely on these structures.

10. Resources

  • TAOCP Vol 1 Chapter 2
  • Effective C

11. Self-Assessment Checklist

  • I can state the invariants for each structure.
  • All tests pass without leaks.

12. Submission / Completion Criteria

Minimum: Stack and queue correct. Full: All structures with tests. Excellence: Custom allocator and iterators.


This guide was generated from LEARN_TAOCP_DEEP_DIVE.md. For the complete learning path, see the parent directory README.