CodeRigor

FREE COURSE / REAL PRACTICE

Data structures & algorithms

Build and use arrays, maps, trees and graphs while reasoning about complexity, traversal and algorithmic contracts.

Free lessons & exercises25 authored lessons10 units

What you’ll work toward

Select and implement data structures, test boundary cases and explain the cost of an approach.

Read an explanation, inspect the diagrams and work through the available checks, debugging exercises and builds. Feedback and prerequisite links guide the next step.

Before you start

The entry lesson has no earlier CodeRigor skill prerequisite. Start there and follow the sequence.

Individual lessons can add prerequisites. The course map shows your actual readiness.

A look inside the first lesson

Big-O and measuring

C++
#include <print>

int main() {
    const int n = 1000;
    long long a = 0, b = 0, c = 0, d = 0;

    for (int i = 0; i < n; ++i)              // n
        for (int j = 0; j < n; ++j) ++a;     //   × n          = n²

    for (int i = 0; i < n; ++i)              // inner runs i times:
        for (int j = 0; j < i; ++j) ++b;     // 0 + 1 + … + (n-1) = n(n-1)/2  → still Θ(n²)

    for (int i = n; i > 0; i /= 2) ++c;      // halving: ⌊log₂ n⌋ + 1 iterations → Θ(log n)

    for (int i = 1; i <= n; ++i)             // inner runs n/i times:
        for (int j = i; j <= n; j += i) ++d; // n(1 + 1/2 + … + 1/n) ≈ n ln n → Θ(n log n)

    std::println("n²={} triangle={} halving={} harmonic={}", a, b, c, d);
}

Your course outline

Follow the lessons in curriculum order. Open a unit to preview the ideas you’ll work with.

UNIT 012 lessons Foundations
  1. LESSON 01.01

    Big-O and measuring

    Count the work a loop does, know the cost of standard operations, and measure growth honestly with a log-log slope.

    Open lesson
  2. LESSON 01.02

    Binary search

    Find the first position where a monotone predicate becomes true, in O(log n), with a loop invariant you can state.

    Open lesson
UNIT 024 lessons Owning things
  1. LESSON 02.01

    Dynamic array (your own vector)

    Separate raw storage from live objects, grow geometrically for amortized O(1) push_back, and keep ownership, aliasing and exceptions straight.

    Open lesson
  2. LESSON 02.02

    Stacks, queues and ring buffers

    LIFO and FIFO, the standard adaptors, and a growable ring buffer with O(1) push and pop at both ends of its life.

    Open lesson
  3. LESSON 02.03

    Hash maps and sets in practice

    Use std::unordered_map and unordered_set for O(1)-average counting, grouping and lookup — and know their traps: operator[] inserts, order is unspecified, the hash decides your complexity.

    Open lesson
  4. LESSON 02.04

    Two pointers and sliding windows

    Replace nested loops with two indices that only move forward: pair sums on sorted data, in-place compaction, and windows that grow and shrink under an invariant — all in O(n).

    Open lesson
UNIT 032 lessons Generic code
  1. LESSON 03.01

    Linked lists

    Node-based sequences: sentinels that delete edge cases, O(1) splicing, iterative reversal and teardown, fast/slow pointers — and why they lose to vectors on modern hardware.

    Open lesson
  2. LESSON 03.02

    Insertion sort and merge sort

    Two stable sorts with opposite strengths: insertion sort (simple, adaptive, great for small or nearly sorted input) and merge sort (O(n log n) always, O(n) extra space).

    Open lesson
UNIT 043 lessons Hashing and heaps
  1. LESSON 04.01

    Hash tables: open addressing

    Build a flat hash map: mix the hash, probe linearly in a power-of-two array, keep the load factor bounded, and delete without breaking probe chains.

    Open lesson
  2. LESSON 04.02

    Binary heaps and priority queues

    Keep the largest element on top of an array-shaped tree: sift up and down in O(log n), build a heap in O(n), and use std::priority_queue without its traps.

    Open lesson
  3. LESSON 04.03

    Partitioning, quicksort and selection

    Partition around a pivot with a precise invariant, then build quicksort and quickselect on it — and make them survive sorted, reversed and all-equal input.

    Open lesson
UNIT 052 lessons Compile time and trees
  1. LESSON 05.01

    Binary trees and binary search trees

    Traverse trees in every order, keep the BST invariant through insert and erase, and never let a degenerate tree overflow your stack.

    Open lesson
  2. LESSON 05.02

    Recursion and backtracking

    Trust the smaller problem; then search a decision tree with choose → explore → un-choose, pruning dead branches and skipping duplicate ones.

    Open lesson
UNIT 063 lessons Sum types, contracts, graphs
  1. LESSON 06.01

    Balanced search trees

    Keep a search tree's height logarithmic with rotations: AVL balance factors, red-black colours, and why std::map is built the way it is.

    Open lesson
  2. LESSON 06.02

    Graphs: representation, BFS and DFS

    Store graphs so neighbours are cheap to visit, then explore them breadth-first (shortest paths, layers) and depth-first (components, cycles, colourings) without blowing the stack.

    Open lesson
  3. LESSON 06.03

    Union-find (disjoint sets)

    Track which elements are connected as you merge groups: a parent array, union by size and path compression give near-constant time per operation.

    Open lesson
UNIT 073 lessons Pipelines and memory
  1. LESSON 07.01

    Shortest paths and topological order

    Dijkstra with a heap and lazy deletion, 0-1 BFS, topological order with Kahn's algorithm, and longest paths in a DAG — with 64-bit distances and cycle detection.

    Open lesson
  2. LESSON 07.02

    Dynamic programming I

    Turn exponential recursion into polynomial time: name the state, write the recurrence, then memoize, tabulate and shrink the table.

    Open lesson
  3. LESSON 07.03

    Fixed-capacity containers: no hidden allocations

    Game- and embedded-style containers in the spirit of EASTL: inline storage with an optional heap overflow, small-buffer optimisation, fixed node pools — and why a frame budget bans surprise allocations.

    Open lesson
UNIT 081 lesson Concurrency
  1. LESSON 08.01

    Lock-free data structures: the Treiber stack

    Build a stack that many threads push and pop without locks: a CAS loop on the head pointer, the ABA problem, and why a popped node can't simply be deleted.

    Open lesson
UNIT 093 lessons Reflection and advanced structures
  1. LESSON 09.01

    Tries and prefix structures

    Store strings as paths in a tree of characters, so prefix questions — how many words start with this, what are the first k completions — cost time proportional to the prefix, not to the dictionary.

    Open lesson
  2. LESSON 09.02

    Range queries: Fenwick and segment trees

    Answer range sums, minimums and friends while the data keeps changing: Fenwick trees for prefix sums, segment trees for any monoid, lazy propagation for range updates — all O(log n).

    Open lesson
  3. LESSON 09.03

    String algorithms: KMP, Z and hashing

    Find patterns in O(n + m) by never re-reading text you already matched: borders and the prefix function (KMP), the Z-function, periods, and rolling hashes that are verified, not trusted.

    Open lesson
UNIT 102 lessons Performance
  1. LESSON 10.01

    Dynamic programming II

    Richer DP states — two strings, intervals, subsets as bitmasks, subtrees, DAGs and digits — plus reconstruction, rolling memory and the classic speed-ups.

    Open lesson
  2. LESSON 10.02

    Intrusive and structure-of-arrays containers

    Containers that don't own their elements' memory or layout: intrusive lists (the link lives in the object), sorted-vector maps, structure-of-arrays tuple vectors, segmented vectors and bit vectors — the EASTL toolbox and when each one pays.

    Open lesson