What are the fundamental asymptotic time and space trade-offs between Adjacency Matrix and Adjacency List graph representations?
The choice between Adjacency Matrix and Adjacency List representations is one of the most fundamental structural design decisions in computer science, dictated by graph density: (1) Adjacency Matrix Architecture: An Adjacency Matrix represents a graph G = (V, E) as a 2D array A of dimensions |V| × |V|, where A[u][v] stores a boolean flag (or edge weight w(u, v)) indicating whether a directed edge exists from vertex u to vertex v. Space Complexity: An Adjacency Matrix strictly consumes Θ(|V|²) memory space regardless of the number of edges |E|. For a sparse graph of 100,000 vertices with 400,000 edges, an adjacency matrix requires 100,000 × 100,000 = 10 billion entries (~10 GB of RAM), even though over 99.99% of entries are zeros. Time Complexity: Querying whether a specific edge (u, v) exists takes strict O(1) time via direct array indexing A[u][v]. However, iterating over all neighbors incident to vertex u requires scanning the entire row A[u], taking Θ(|V|) time regardless of whether u has 1 neighbor or 1000 neighbors. Consequently, standard graph algorithms (BFS, DFS, Dijkstra) execute in Θ(|V|²) time when implemented on an Adjacency Matrix. (2) Adjacency List Architecture: An Adjacency List represents G as an array of |V| linked sequences (or dynamic vectors), where list Adj[u] contains only the vertices v such that edge (u, v) ∈ E. Space Complexity: An Adjacency List consumes Θ(|V| + |E|) memory space, perfectly proportional to the actual size of the graph. For the same 100,000 vertex graph with 400,000 edges, an adjacency list requires only ~100,000 head pointers + 400,000 edge nodes (~6.4 MB of RAM)—a 1500-fold memory reduction! Time Complexity: Testing whether edge (u, v) exists requires searching the list Adj[u], taking O(deg(u)) time. However, iterating over all neighbors of vertex u takes optimal O(deg(u)) time. Summing over all vertices during a complete traversal yields ∑_{u ∈ V} deg(u) = |E| operations, allowing BFS and DFS to run in optimal Θ(|V| + |E|) time.