Code Snippets
/

Graph Adjacency List in Python

Graph Adjacency List in Python

An adjacency list represents a graph as 'for each node, the nodes it connects to'. In Python the right shape is `defaultdict(list)`: insertion is one line, traversal is one nested loop, and you do not pay for the V*V matrix. This entry covers building the list, walking it with DFS, and the directed vs undirected detail that bites every newcomer.

Python
Medium
3 snippets
graphs
graph-representation
data-structures
py-standard-library

736 views

9

from collections import defaultdict

def build_graph(edges, *, directed=False):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)
        if not directed:
            graph[v].append(u)
    return graph

edges = [(1, 2), (1, 3), (2, 4), (3, 4), (4, 5)]

undirected = build_graph(edges)
for node in sorted(undirected):
    print(node, '->', sorted(undirected[node]))
# 1 -> [2, 3]
# 2 -> [1, 4]
# 3 -> [1, 4]
# 4 -> [2, 3, 5]
# 5 -> [4]

directed = build_graph(edges, directed=True)
print()
for node in sorted(directed):
    print(node, '->', directed[node])
# 1 -> [2, 3]
# 2 -> [4]
# 3 -> [4]
# 4 -> [5]

defaultdict(list) removes the boilerplate of 'check if the key exists, create an empty list if not, then append'. For undirected graphs, every edge (u, v) is added twice (u -> v and v -> u) so both endpoints can find each other. The directed=True switch flips that off. The space cost is O(V + E), not O(V^2) like an adjacency matrix, which is why adjacency lists are the default representation for sparse graphs. Sorting the neighbor lists is purely for readable output; in production you keep insertion order so traversal is deterministic.