A graph is nodes plus “who talks to whom.”
Interview default storage: an adjacency list — dict/map from node → list of neighbors. Undirected edge u—v means append both ways. Matrices are fine for dense n×n; most LC graphs are sparse, so lists win on memory.
Tiny example: edges (0,1), (0,2), (1,3), (2,3). After both directions, node 0’s list is [1, 2], node 3’s is [1, 2]. Same diamond you see in the visualizer.
Dry run
start: adj = {}
(0,1) → adj[0]+=1, adj[1]+=0
(0,2) → adj[0]+=2, adj[2]+=0
(1,3) → adj[1]+=3, adj[3]+=1
(2,3) → adj[2]+=3, adj[3]+=2
adj[0] = [1, 2]
adj[1] = [0, 3]
adj[2] = [0, 3]
adj[3] = [1, 2]
Complexity
Build is O(E) time and O(V + E) space. Looking up neighbors of one node is O(degree). An n×n matrix would be O(V²) space even if the graph is sparse — usually a bad default.
Common mistakes
- Adding only one direction on an undirected graph — BFS/DFS silently misses half the edges.
- Assuming nodes are always
0..n-1; sometimes you only get an edge list and must invent keys as you go. - Using a matrix “because graphs = matrices in class” when V is thousands and E is tiny.
Tip: always ask “directed or undirected?” before coding. Forgetting the reverse edge is a classic silent bug.
Your turn: fill both directions. Expect 0 -> [1, 2] and 3 -> [1, 2].
Watch it run
1 / 9Undirected graph as adj list
BFS from node 0. Queue starts with 0.
Output
Press Run to execute your code.