Python 3.11+
The examples use standard Python containers and syntax available in the supported browser runtime; Graph itself is taught as an interview abstraction.
Verify in Python docs(opens in a new tab)Vertices and edges representing arbitrary relationships, reachability, paths, and dependencies. This guide turns that contract into deliberate Python operations, interview invariants, and a runnable practice loop.
Recognize it when
Consider Graph when the prompt's constraints and required operations match this shape: Vertices and edges representing arbitrary relationships, reachability, paths, and dependencies.

Sign in to save your code and progress across devices. The lesson and problem statement remain public.
A graph separates entities (vertices) from relationships (edges). Unlike a tree, a vertex may have multiple incoming paths, and cycles may return to already discovered state. Direction, weight, parallel edges, and self-loops are contract decisions, not implementation details.
For an undirected edge, record both directions; for a directed edge, record only source to target. Degree, reachability, and cycle meaning change with direction. Normalize duplicate-edge policy before building storage so traversal and complexity claims match the logical graph.
An adjacency list uses O(V + E) space and enumerates actual neighbors efficiently. An adjacency matrix uses O(V squared) space but tests a particular edge in O(1). Compare density and query type rather than choosing by habit. Sets deduplicate neighbors; lists preserve repeated edges and input order.
def build_graph_representations(node_count, edges):
adjacency = [set() for _ in range(node_count)]
matrix = [[0 for _ in range(node_count)] for _ in range(node_count)]
for left, right in edges:
adjacency[left].add(right)
adjacency[right].add(left)
matrix[left][right] = 1
matrix[right][left] = 1
return [[sorted(neighbors) for neighbors in adjacency], matrix]Implement build_graph_representations(node_count, edges). Deduplicate undirected edges and return sorted adjacency lists plus an adjacency matrix.
Loading interactive editor.If it does not appear, the reference and starter code remain readable.Reload editor
DFS and BFS need visited state because a graph can contain cycles and converging paths. Mark when an item is discovered to prevent duplicate frontier entries. To find components, begin a traversal from each still-unvisited vertex; one traversal consumes exactly one connected component.
def component_sizes(node_count, edges):
adjacency = [[] for _ in range(node_count)]
for left, right in edges:
adjacency[left].append(right)
adjacency[right].append(left)
visited = set()
sizes = []
for start in range(node_count):
if start in visited:
continue
visited.add(start)
stack = [start]
size = 0
while stack:
node = stack.pop()
size += 1
for neighbor in adjacency[node]:
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)
sizes.append(size)
return sizesImplement component_sizes(node_count, edges). Include isolated vertices and return undirected component sizes in increasing start-vertex order.
Loading interactive editor.If it does not appear, the reference and starter code remain readable.Reload editor
The deque reference(opens in a new tab) supports the BFS frontier; a list supports iterative DFS as a stack. Both traversals are O(V + E) with adjacency lists because each vertex and stored edge is examined a bounded number of times.
Choose a graph for arbitrary relationships, multiple paths, cycles, or dependencies. Choose an implicit grid when neighbors derive from coordinates instead of stored edges. Choose a rooted tree when every non-root node has one parent and there is a unique root path; then parent state can replace a general visited set. Compare BFS, DFS, topological order, and shortest-path algorithms only after the edge contract is known.
State V, E, direction, weight, and duplicate policy before coding. Define what visited means and when it changes. Give O(V + E) for adjacency-list traversal, noting that undirected edges are stored twice, and distinguish frontier space from representation space.
Build Graph Representations(opens in a new tab) forces a storage decision. Course Schedule(opens in a new tab) adds direction and cycle detection to model prerequisites.
Python 3.11+
The examples use standard Python containers and syntax available in the supported browser runtime; Graph itself is taught as an interview abstraction.
Verify in Python docs(opens in a new tab)Interview bounds depend on the stated representation, input model, and real Python operations.
| Operation | Average | Worst | Interview note |
|---|---|---|---|
| Core Graph workflow | O(V^2 + E + V log V) | O(V^2 + E + V log V) | Accumulate canonical neighbors in per-node sets while writing symmetric matrix cells, then sort each set for deterministic output. |
O(V^2 + E) for the demonstrated Graph workflow.
These are the mistakes most likely to survive a happy-path example and fail a boundary case.
Placeholder code cannot preserve the Graph invariant or satisfy the public contract.
Prevent it: Implement the smallest complete transition and run the public tests before adding optimizations.
Avoid
def build_graph_representations(node_count, edges):
passUse instead
def build_graph_representations(node_count, edges):
adjacency = [set() for _ in range(node_count)]
matrix = [[0 for _ in range(node_count)] for _ in range(node_count)]
for left, right in edges:
adjacency[left].add(right)
adjacency[right].add(left)
matrix[left][right] = 1
matrix[right][left] = 1
return [[sorted(neighbors) for neighbors in adjacency], matrix]Where you will hit this: Build Graph Representations(opens in a new tab)
Records only the listed edge direction, producing a directed graph instead of the required undirected graph.
Prevent it: Keep this invariant visible while editing: State the precise Graph invariant before coding and preserve it after every update, traversal step, or recursive return.
Avoid
def build_graph_representations(node_count, edges):
adjacency = [[] for _ in range(node_count)]
matrix = [[0] * node_count for _ in range(node_count)]
for left, right in edges:
adjacency[left].append(right)
matrix[left][right] = 1
return [adjacency, matrix]Use instead
def build_graph_representations(node_count, edges):
adjacency = [set() for _ in range(node_count)]
matrix = [[0 for _ in range(node_count)] for _ in range(node_count)]
for left, right in edges:
adjacency[left].add(right)
adjacency[right].add(left)
matrix[left][right] = 1
matrix[right][left] = 1
return [[sorted(neighbors) for neighbors in adjacency], matrix]Where you will hit this: Build Graph Representations(opens in a new tab)
Handle empty state, duplicate values, aliasing, and serialization boundaries explicitly in tests.
Prevent it: Account for every slice, copy, sort, membership test, and container mutation before claiming O(V^2 + E + V log V).
Avoid
def build_graph_representations(node_count, edges):
adjacency = [[] for _ in range(node_count)]
matrix = [[0] * node_count for _ in range(node_count)]
for left, right in edges:
adjacency[left].append(right)
matrix[left][right] = 1
return [adjacency, matrix]Use instead
def build_graph_representations(node_count, edges):
adjacency = [set() for _ in range(node_count)]
matrix = [[0 for _ in range(node_count)] for _ in range(node_count)]
for left, right in edges:
adjacency[left].add(right)
adjacency[right].add(left)
matrix[left][right] = 1
matrix[right][left] = 1
return [[sorted(neighbors) for neighbors in adjacency], matrix]Where you will hit this: Course Schedule(opens in a new tab)
Official Python documentation supports language behavior. Canonical problem pages provide additional practice context.
python-docs · checked 2026-07-12