Graphs

A Graph is a collection of Vertices/Nodes (denoted as ) and Edges (denoted as ) that connect pairs of vertices.

If the Edges have a direction, then we have a Directed Graph:

exampleaabba->bcca->cddb->dc->d

Otherwise, we have an Undirected Graph:

exampleaabba--bcca--cddb--dc--d

For both graphs, the nodes are a, b, c and d. However, the edges differ between the two.

In fact, if we group the edges for the single nodes, we would have the following ones for the Directed graph:

a -> {b, c} b -> {d} c -> {d} d -> {}

And these for the Undirected one:

a -> {b, c} b -> {a, d} c -> {a, d} d -> {b, c}

So, we simply mapped the nodes with a list of so called Neighbors.

Connected and Disconnected Graphs

The previous Graphs were all Connected, since there was a path between each Node. However, we can also have Disconnected ones:

exampleccddc--daabba--b

In this case, there is no connection between the nodes a, b and c, d.

Representation

The most common way to represent a Graph is within an Adjacency List, which can be a Python dictionary that maps the Node to the set of Neighbors.

Usually, a list of Edges is provided, where each element is either a list or a tuple of two elements. For example:

edges = [ ["a", "b"], ["a", "c"], ]

Adjacency List for a Directed Graph

Since the Graph is Directed, we just need to consider a single dependency between two nodes (a single add call).

exampleaabba->bcca->c

from collections import defaultdict edges = [ ("a", "b"), ("a", "c"), ] graph = defaultdict(list) for a, b in edges: graph[a].append(b)

Adjacency List for an Undirected Graph

For the Undirected one, we need a double dependency between the two nodes. In fact, node a has b as Neighbor, and vice versa.

exampleaabba--bcca--c

from collections import defaultdict edges = [ ("a", "b"), ("a", "c"), ] graph = defaultdict(list) for a, b in edges: graph[a].append(b) graph[b].append(a) # the only difference compared to the other implementation