PAITS Cheat Sheet Algorithms Intermediate Updated 2026-10-23

Graph Algorithms: Dijkstra, A*, Topological Sort & Union-Find

Shortest-path selection matrix, Kahn's topological sort, Disjoint Set Union (DSU), and Tarjan's SCC.

Executive Summary: Complete decision matrix and production Python implementations for graph traversal, shortest paths, cycle detection, minimum spanning trees, and union-find with path compression.

Key Equations & Invariants

Dijkstra Priority Queue Complexity
T(V, E) = O\bigl((V + E)\log V\bigr)

Requires non-negative edge weights (w(u, v) >= 0) so settled vertices never need relaxation.

A* Heuristic Admissibility & Consistency
0 \le h(u) \le d^*(u, t) \quad \text{and} \quad h(u) \le w(u, v) + h(v)

Admissible and consistent heuristics guarantee A* expands the minimum number of nodes.

1. Shortest-Path Algorithm Selection Matrix

Select the shortest-path algorithm based on edge weight constraints and query cardinality.

AlgorithmEdge WeightsTime ComplexitySpaceBest For
BFSUnweighted (w = 1)O(V + E)O(V)Minimum hops in unweighted graphs
0-1 BFS (Deque)w in {0, 1}O(V + E)O(V)Grid mazes with free/cost-1 edges
Dijkstra (Min-Heap)w >= 0O((V + E) log V)O(V)Single-source non-negative road/network graphs
Bellman-FordAny real w (detects neg cycles)O(V * E)O(V)Arbitrage detection & K-stop constrained paths
Floyd-WarshallAny real w (no neg cycles)O(V^3)O(V^2)All-pairs shortest paths on dense graphs (V <= 400)
Lazy-Deletion Dijkstra & Disjoint Set Union (Path Compression + Rank)python
import heapq

def dijkstra(n: int, adj: list[list[tuple[int, int]]], src: int) -> list[ float]:
    dist = [float('inf')] * n
    dist[src] = 0.0
    pq: list[tuple[float, int]] = [(0.0, src)]
    while pq:
        d, u = heapq.heappop(pq)
        if d > dist[u]:
            continue  # Skip stale heap entry
        for v, weight in adj[u]:
            if dist[u] + weight < dist[v]:
                dist[v] = dist[u] + weight
                heapq.heappush(pq, (dist[v], v))
    return dist

class UnionFind:
    def __init__(self, n: int) -> None:
        self.parent = list(range(n))
        self.size = [1] * n

    def find(self, x: int) -> int:
        while x != self.parent[x]:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x

Common Production & Interview Pitfalls

Frequently Asked Questions

Q: How do I detect a cycle in a directed graph vs an undirected graph?

For a directed graph, use Kahn's in-degree topological sort (if processed count < V, a cycle exists) or 3-color DFS (White/Gray/Black). For an undirected graph, use Union-Find or parent-tracked DFS.