Core Algorithms Cheat Sheet: Sorting, Graphs, Greedy, DP, Strings, NP

November 20, 2025

This is an overview of core algorithms and analysis techniques likely seen in CS: sorting, graphs, unionfind and MST, greedy vs dynamic programming, string matching, and an intro to NP.

Asymptotic Analysis & Techniques

Sorting Algorithms

Insertion Sort

void insertion_sort(std::vector<int>& v) {
    int n = (int)v.size();
    for (int i = 1; i < n; ++i) {
        int key = v[i];
        int j = i - 1;
        while (j >= 0 && v[j] > key) {
            v[j + 1] = v[j];
            --j;
        }
        v[j + 1] = key;
    }
}

Mergesort

void merge(std::vector<int>& a, int l, int m, int r) {
    std::vector<int> tmp;
    int i = l, j = m + 1;
    while (i <= m && j <= r) {
        if (a[i] <= a[j]) tmp.push_back(a[i++]);
        else              tmp.push_back(a[j++]);
    }
    while (i <= m) tmp.push_back(a[i++]);
    while (j <= r) tmp.push_back(a[j++]);
    for (int k = 0; k < (int)tmp.size(); ++k) {
        a[l + k] = tmp[k];
    }
}

void mergesort(std::vector<int>& a, int l, int r) {
    if (l >= r) return;
    int m = l + (r - l) / 2;
    mergesort(a, l, m);
    mergesort(a, m + 1, r);
    merge(a, l, m, r);
}

Quicksort

int partition(std::vector<int>& a, int l, int r) {
    int pivot = a[r];
    int i = l;
    for (int j = l; j < r; ++j) {
        if (a[j] < pivot) {
            std::swap(a[i], a[j]);
            ++i;
        }
    }
    std::swap(a[i], a[r]);
    return i;
}

void quicksort(std::vector<int>& a, int l, int r) {
    if (l >= r) return;
    int p = partition(a, l, r);
    quicksort(a, l, p - 1);
    quicksort(a, p + 1, r);
}

Heapsort

void heapify(std::vector<int>& a, int n, int i) {
    int largest = i;
    int L = 2 * i + 1, R = 2 * i + 2;
    if (L < n && a[L] > a[largest]) largest = L;
    if (R < n && a[R] > a[largest]) largest = R;
    if (largest != i) {
        std::swap(a[i], a[largest]);
        heapify(a, n, largest);
    }
}

void heapsort(std::vector<int>& a) {
    int n = (int)a.size();
    for (int i = n / 2 - 1; i >= 0; --i)
        heapify(a, n, i);
    for (int i = n - 1; i > 0; --i) {
        std::swap(a[0], a[i]);
        heapify(a, i, 0);
    }
}

Graphs & Traversals

BFS Example (Shortest Path in Unweighted Graph)

std::vector<int> bfs_shortest_paths(
    const std::vector<std::vector<int>>& adj, int start) {

    int n = (int)adj.size();
    std::vector<int> dist(n, -1);
    std::queue<int> q;
    dist[start] = 0;
    q.push(start);

    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int v : adj[u]) {
            if (dist[v] == -1) {
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
    }
    return dist;
}

Dijkstra's Algorithm (Shortest Path)

using Edge = std::pair<int,int>; // (neighbor, weight)

std::vector<int> dijkstra(
    const std::vector<std::vector<Edge>>& adj, int src) {

    const int INF = 1e9;
    int n = (int)adj.size();
    std::vector<int> dist(n, INF);
    dist[src] = 0;

    using State = std::pair<int,int>; // (dist, node)
    std::priority_queue<State, std::vector<State>,
                        std::greater<State>> pq;
    pq.push({0, src});

    while (!pq.empty()) {
        auto [d, u] = pq.top(); pq.pop();
        if (d != dist[u]) continue; // stale
        for (auto [v, w] : adj[u]) {
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                pq.push({dist[v], v});
            }
        }
    }
    return dist;
}

unionfind (Disjoint Set Union, DSU)

struct DSU {
    std::vector<int> parent, sz;

    DSU(int n) : parent(n), sz(n, 1) {
        std::iota(parent.begin(), parent.end(), 0);
    }

    int find(int x) {
        if (parent[x] != x)
            parent[x] = find(parent[x]); // path compression
        return parent[x];
    }

    bool unite(int a, int b) {
        a = find(a); b = find(b);
        if (a == b) return false;
        if (sz[a] < sz[b]) std::swap(a, b);
        parent[b] = a;
        sz[a] += sz[b];
        return true;
    }
};

Minimum Spanning Trees (MST)

For a connected, weighted, undirected graph, an MST is a subset of edges connecting all vertices with minimum total weight and no cycles.

Kruskal's Algorithm (with unionfind)

struct Edge {
    int u, v, w;
};

int kruskal(int n, std::vector<Edge> edges) {
    std::sort(edges.begin(), edges.end(),
              [](const Edge& a, const Edge& b) {
        return a.w < b.w;
    });

    DSU dsu(n);
    int total = 0;
    for (auto &e : edges) {
        if (dsu.unite(e.u, e.v)) {
            total += e.w;
        }
    }
    return total;
}

Prim's Algorithm (Alternative MST)

Topological Sort

// Kahn's algorithm
std::vector<int> topo_sort(const std::vector<std::vector<int>>& adj) {
    int n = (int)adj.size();
    std::vector<int> indeg(n, 0);
    for (int u = 0; u < n; ++u)
        for (int v : adj[u])
            ++indeg[v];

    std::queue<int> q;
    for (int i = 0; i < n; ++i)
        if (indeg[i] == 0) q.push(i);

    std::vector<int> order;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        order.push_back(u);
        for (int v : adj[u]) {
            if (--indeg[v] == 0)
                q.push(v);
        }
    }
    // If order.size() < n, graph had a cycle
    return order;
}

Greedy Algorithms

Greedy Example: Interval Scheduling

Choose maximum number of non overlapping intervals by sorting by end time.

struct Interval {
    int start, end;
};

int max_non_overlapping(std::vector<Interval> intervals) {
    std::sort(intervals.begin(), intervals.end(),
              [](const Interval& a, const Interval& b) {
        return a.end < b.end;
    });

    int count = 0;
    int last_end = std::numeric_limits<int>::min();
    for (auto &iv : intervals) {
        if (iv.start >= last_end) {
            ++count;
            last_end = iv.end;
        }
    }
    return count;
}

Dynamic Programming (DP)

DP Example: 0/1 Knapsack (Bottom Up)

int knapsack(const std::vector<int>& w,
             const std::vector<int>& v,
             int W) {
    int n = (int)w.size();
    std::vector<std::vector<int>> dp(n + 1, std::vector<int>(W + 1, 0));

    for (int i = 1; i <= n; ++i) {
        for (int cap = 0; cap <= W; ++cap) {
            dp[i][cap] = dp[i - 1][cap]; // skip item
            if (cap - w[i - 1] >= 0) {
                dp[i][cap] = std::max(dp[i][cap],
                                      dp[i - 1][cap - w[i - 1]] + v[i - 1]);
            }
        }
    }
    return dp[n][W];
}

String Matching

Naive String Matching

std::vector<int> find_naive(
    const std::string& text, const std::string& pat) {

    std::vector<int> pos;
    int n = (int)text.size();
    int m = (int)pat.size();
    for (int i = 0; i + m <= n; ++i) {
        int j = 0;
        while (j < m && text[i + j] == pat[j]) {
            ++j;
        }
        if (j == m) pos.push_back(i);
    }
    return pos;
}

KMP Prefix Function

std::vector<int> build_lps(const std::string& pat) {
    int m = (int)pat.size();
    std::vector<int> lps(m, 0);
    int len = 0;
    for (int i = 1; i < m; ) {
        if (pat[i] == pat[len]) {
            lps[i++] = ++len;
        } else if (len > 0) {
            len = lps[len - 1];
        } else {
            lps[i++] = 0;
        }
    }
    return lps;
}

std::vector<int> kmp_search(
    const std::string& text, const std::string& pat) {

    std::vector<int> pos;
    if (pat.empty()) return pos;

    auto lps = build_lps(pat);
    int i = 0, j = 0;
    while (i < (int)text.size()) {
        if (text[i] == pat[j]) {
            ++i; ++j;
            if (j == (int)pat.size()) {
                pos.push_back(i - j);
                j = lps[j - 1];
            }
        } else if (j > 0) {
            j = lps[j - 1];
        } else {
            ++i;
        }
    }
    return pos;
}

Intro to NP, NP Complete, NP Hard

Takeaways

← Back to blog