Graphs: an illustrated study guide
1. Where graph theory began
A graph is a network of vertices (nodes) joined by edges (links). Graph theory began in 1736, when Leonhard Euler solved the Bridges of Königsberg problem (the city is now Kaliningrad). The city sat on the river Pregel, with an island (Kneiphof) and seven bridges. The question: can you take a walk that crosses every bridge exactly once and returns to where you started?
Euler's insight: the shape of the land doesn't matter, only how the pieces connect. Every time you walk through a land mass you use one bridge to enter and another to leave, so each vertex needs an even degree for a closed walk using every edge once to exist.
| Land mass | Bridges (degree) | Parity |
|---|---|---|
| A (Kneiphof island) | 5 | odd |
| B (south bank) | 3 | odd |
| C (north bank) | 3 | odd |
| D (east bank) | 3 | odd |
2. Vocabulary and types of graphs
Basic terms: a path is a sequence of vertices joined by edges; a cycle is a path that returns to its start. The degree of a vertex is the number of edges incident on it. In a directed graph the degree splits into in-degree (arrows arriving) and out-degree (arrows leaving).
Graphs are classified along several independent axes. A single graph is usually described by combining them, e.g. “a weighted, undirected, connected graph”.
| Axis | Options | Meaning |
|---|---|---|
| Direction | directed / undirected | Do edges have a direction? |
| Weight | weighted / unweighted | Do edges carry a value (cost, distance)? |
| Connectivity | connected / disconnected | Is there a path between every pair of vertices? |
| Density | complete / incomplete | Is every pair of vertices joined by an edge? |
| Cycles | cyclic / acyclic | Does the graph contain a cycle? (mostly discussed for directed graphs) |
| Structure | bipartite | Can the vertices be split into two sets with every edge between them? |
Undirected
Edges have no direction: A–B is the same as B–A.
Directed (digraph)
Edges are one-way arrows, like “follows” on social media.
Weighted
Each edge carries a number: distance, cost, time.
Connected
There is a path between every pair of vertices.
Disconnected
Some vertices cannot reach each other (two components).
Complete (K₅)
Every vertex is joined to every other. Kn has n(n−1)/2 edges.
Cyclic
Following the arrows you can return to the start (A→B→C→D→A).
Acyclic (DAG)
Directed with no cycle. Used for prerequisites and schedules.
Bipartite
Vertices split into two sets; every edge crosses between them.
3. Bipartite graphs, isomorphism and cliques
Bipartite graphs
A simple graph is bipartite if its vertex set V can be split into two disjoint sets V₁ and V₂ so that every edge joins a vertex of V₁ to a vertex of V₂, and no edge joins two vertices in the same set. The pair (V₁, V₂) is a bipartition.
Colouring test. A graph is bipartite if and only if each vertex can be given one of two colours so that no two adjacent vertices share a colour. In practice: colour the start vertex, run BFS, and give each newly found vertex the opposite colour from its parent. If you ever meet an edge whose ends already have the same colour, the graph is not bipartite. Equivalently, a bipartite graph has no odd-length cycle.
Isomorphism
Two graphs are isomorphic if they contain the same number of vertices connected in the same way; one is a re-labelled or re-drawn copy of the other. Quick necessary checks: same number of vertices, same number of edges, and the same vertex degrees. If any differ, the graphs are not isomorphic. Passing all of them does not prove isomorphism.
| Check | G (6-cycle) | H (two triangles) |
|---|---|---|
| Vertices | 6 | 6 |
| Edges | 6 | 6 |
| Degrees | all 2 | all 2 |
| Connected? | yes | no |
Cliques
A clique is a subset of vertices in which every vertex is connected to every other, i.e. a complete subgraph. The size of the largest clique is the clique number. Finding the maximum clique is a significant (and computationally hard) problem. In the US-cities graph below, Houston–Dallas–Atlanta form a triangle, a clique of size 3, and no 4-clique exists.
4. Eulerian and Hamiltonian cycles
Two look-alike questions with very different difficulty.
| Eulerian | Hamiltonian | |
|---|---|---|
| Must visit | every edge exactly once | every vertex exactly once |
| Cycle / circuit | returns to the starting vertex | returns to the starting vertex |
| Path version | start and end at different vertices | start and end at different vertices |
| Test | Easy: all degrees even (and connected) ⇒ Eulerian cycle. For an Euler path: exactly 0 or 2 odd-degree vertices. | Hard: no simple degree rule and no known efficient general test. |
| Example | Königsberg: four odd vertices ⇒ impossible | Visiting every town once and coming home |
5. Representing a graph in a program
Before an algorithm can run, the graph must be stored. There are two standard choices.
- Adjacency matrix: a V×V table where entry [i][j] is 1 (or the edge weight) if there is an edge from i to j, otherwise 0.
- Adjacency list: an array indexed by vertex, where each entry is a (linked) list of that vertex's neighbours.
Adjacency matrix
| 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|
| 1 | 0 | 1 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 | 1 | 0 | 0 |
| 3 | 1 | 1 | 0 | 0 | 1 | 0 |
| 4 | 0 | 0 | 0 | 0 | 0 | 1 |
| 5 | 0 | 1 | 0 | 1 | 0 | 1 |
| 6 | 0 | 0 | 0 | 0 | 0 | 0 |
Row = from, column = to. Row 3 reads: 3→1, 3→2, 3→5.
Adjacency lists
| Vertex | Adjacency list |
|---|---|
| 1 | 2 |
| 2 | 4 |
| 3 | 1 → 2 → 5 |
| 4 | 6 |
| 5 | 2 → 4 → 6 |
| 6 | ∅ |
Undirected graphs give a symmetric matrix
| A | B | C | D | E | |
|---|---|---|---|---|---|
| A | 0 | 1 | 0 | 1 | 0 |
| B | 1 | 0 | 1 | 1 | 0 |
| C | 0 | 1 | 0 | 0 | 1 |
| D | 1 | 1 | 0 | 0 | 1 |
| E | 0 | 0 | 1 | 1 | 0 |
Every edge appears twice (A–B sets both [A][B] and [B][A]), so the matrix equals its own transpose.
Which one to choose?
| Adjacency matrix | Adjacency list | |
|---|---|---|
| Memory | O(V²) | O(V + E) |
| Is there an edge u→v? | O(1) | O(degree of u) |
| List all neighbours of u | O(V) | O(degree of u) |
| Best for | dense graphs, frequent edge look-ups | sparse graphs, traversals |
Most real graphs (road maps, social networks) are sparse, so adjacency lists are the usual default.
6. Graph traversals: DFS and BFS
A traversal visits every vertex reachable from a starting vertex exactly once. The two classic strategies differ only in the data structure that remembers where to go next: a stack (last in, first out) gives depth-first search, a queue (first in, first out) gives breadth-first search. When several neighbours are available, they are taken in alphabetical order.
Depth-first search (DFS)
- Create a stack.
- Pick a start vertex, make it current, mark it visited, push it.
- If the current vertex has an unvisited neighbour (in order), visit it: make it current, mark it, push it.
- Otherwise pop a vertex off the stack and make it current.
- Repeat 3–4 until the stack is empty.
DFS plunges as deep as it can, then backtracks. Good for cycle detection, connected components, topological sort and maze solving.
Breadth-first search (BFS)
- Create a queue.
- Pick a start vertex, make it current, mark it visited.
- Visit each unvisited neighbour of the current vertex in order: mark it and enqueue it.
- When none remain, dequeue a vertex and make it current.
- Repeat 3–4 until the queue is empty.
BFS explores in rings of increasing distance from the start. It finds the path with the fewest edges in an unweighted graph.
Try it: step through both on the same tree
DFS trace
| Event | Stack |
|---|---|
| Start: visit A, mark it visited, push it on the stack. | A |
| Visit B: an unvisited neighbour of A. Mark it and push it. | A B |
| Visit F: an unvisited neighbour of B. Mark it and push it. | A B F |
| Visit H: an unvisited neighbour of F. Mark it and push it. | A B F H |
| H has no unvisited neighbours: pop it. F becomes current. | A B F |
| F has no unvisited neighbours: pop it. B becomes current. | A B |
| B has no unvisited neighbours: pop it. A becomes current. | A |
| Visit C: an unvisited neighbour of A. Mark it and push it. | A C |
| C has no unvisited neighbours: pop it. A becomes current. | A |
| Visit D: an unvisited neighbour of A. Mark it and push it. | A D |
| Visit G: an unvisited neighbour of D. Mark it and push it. | A D G |
| Visit I: an unvisited neighbour of G. Mark it and push it. | A D G I |
| I has no unvisited neighbours: pop it. G becomes current. | A D G |
| G has no unvisited neighbours: pop it. D becomes current. | A D |
| D has no unvisited neighbours: pop it. A becomes current. | A |
| Visit E: an unvisited neighbour of A. Mark it and push it. | A E |
| E has no unvisited neighbours: pop it. A becomes current. | A |
| A has no unvisited neighbours: pop it. Stack empty: done. | — |
BFS trace
| Event | Queue |
|---|---|
| Start: visit A and mark it. A is the current vertex. | — |
| Visit B, a neighbour of A. Mark it and enqueue it. | B |
| Visit C, a neighbour of A. Mark it and enqueue it. | B C |
| Visit D, a neighbour of A. Mark it and enqueue it. | B C D |
| Visit E, a neighbour of A. Mark it and enqueue it. | B C D E |
| No more unvisited neighbours: dequeue B and make it current. | C D E |
| Visit F, a neighbour of B. Mark it and enqueue it. | C D E F |
| No more unvisited neighbours: dequeue C and make it current. | D E F |
| No more unvisited neighbours: dequeue D and make it current. | E F |
| Visit G, a neighbour of D. Mark it and enqueue it. | E F G |
| No more unvisited neighbours: dequeue E and make it current. | F G |
| No more unvisited neighbours: dequeue F and make it current. | G |
| Visit H, a neighbour of F. Mark it and enqueue it. | G H |
| No more unvisited neighbours: dequeue G and make it current. | H |
| Visit I, a neighbour of G. Mark it and enqueue it. | H I |
| No more unvisited neighbours: dequeue H and make it current. | I |
| No more unvisited neighbours: dequeue I and make it current. | — |
| I is finished and the queue is empty: done. | — |
Result: DFS order A, B, F, H, C, D, G, I, E; BFS order A, B, C, D, E, F, G, H, I. Both visit every vertex and take O(V + E) time with an adjacency list.
| DFS | BFS | |
|---|---|---|
| Data structure | stack (or recursion) | queue |
| Shape of exploration | deep, then backtrack | level by level |
| Shortest path (unweighted) | not guaranteed | guaranteed |
| Time | O(V + E) | O(V + E) |
7. Case study: a graph of US cities
Your 14 Nov lecture modelled 12 US cities as a graph and stored it as text (graph.txt). The file format is: the number of vertices (12), the city names (vertices 0–11), the number of edge entries (46), then one line per entry from to weight, followed by some test queries (Houston; Dallas → Atlanta; Denver). Every road appears in both directions with weight 1, so the graph is undirected and unweighted with 46 / 2 = 23 edges.
graph.txt; positions approximate your “Modeling Using Graphs” map.Adjacency list built from the file
| City | Neighbours |
|---|---|
| Seattle | San Francisco, Denver, Chicago |
| San Francisco | Seattle, Los Angeles, Denver |
| Los Angeles | San Francisco, Denver, Kansas City, Dallas |
| Denver | Seattle, San Francisco, Los Angeles, Kansas City, Chicago |
| Kansas City | Los Angeles, Denver, Chicago, New York, Atlanta, Dallas |
| Chicago | Seattle, Denver, Kansas City, Boston, New York |
| Boston | Chicago, New York |
| New York | Kansas City, Chicago, Boston, Atlanta |
| Atlanta | Kansas City, New York, Miami, Dallas, Houston |
| Miami | Atlanta, Houston |
| Dallas | Los Angeles, Kansas City, Atlanta, Houston |
| Houston | Atlanta, Miami, Dallas |
Traversals starting from Houston
BFS order: Houston, Atlanta, Miami, Dallas, Kansas City, New York, Los Angeles, Denver, Chicago, Boston, San Francisco, Seattle.
DFS order: Houston, Atlanta, Kansas City, Los Angeles, San Francisco, Seattle, Denver, Chicago, Boston, New York, Dallas, Miami.
Neighbours are taken in vertex-number order, so the results depend on that numbering.
Fewest-stops route from Dallas to Atlanta (a BFS query): Dallas → Atlanta (1 edge). From Houston to Denver: Houston → Atlanta → Kansas City → Denver (3 edges).
8. Measuring a graph
Metrics answer two questions: how well connected is the whole graph? and which vertices matter most? The right-hand column applies each metric to the US-cities graph (V = 12, E = 23).
| Metric | Definition | US cities graph |
|---|---|---|
| Edge density | actual edges ÷ maximum possible edges, V(V−1)/2 for undirected | 23/66 = 0.348 |
| Node connectivity | minimum number of vertices whose removal disconnects the graph (or leaves a single vertex) | 2 (removing Chicago and New York cuts Boston off) |
| Algebraic connectivity | second-smallest eigenvalue of the graph Laplacian L = D − A. It is greater than 0 iff the graph is connected; larger means more robustly connected | 0.758 (positive, so connected) |
| Degree centrality | neighbours ÷ neighbours possible in a complete graph (V−1). Directed graphs: in-degree (“followers”) and out-degree (“following”) | Kansas City highest: 6/11 = 0.55 |
| Betweenness centrality | for each vertex v, sum over all pairs (s,t) of (shortest s–t paths through v) ÷ (all shortest s–t paths). High values mark “gatekeepers” or bridges | Kansas City highest (14.6) |
| Clique number | size of the largest complete subgraph | 3 (Houston–Dallas–Atlanta) |
Degree and betweenness for each city
| City | Degree | Degree centrality | Betweenness |
|---|---|---|---|
| Kansas City | 6 | 6/11 = 0.55 | 14.6 |
| Atlanta | 5 | 5/11 = 0.45 | 12.7 |
| Chicago | 5 | 5/11 = 0.45 | 9.0 |
| New York | 4 | 4/11 = 0.36 | 6.6 |
| Dallas | 4 | 4/11 = 0.36 | 6.1 |
| Denver | 5 | 5/11 = 0.45 | 6.0 |
| Los Angeles | 4 | 4/11 = 0.36 | 5.8 |
| Seattle | 3 | 3/11 = 0.27 | 1.2 |
| Houston | 3 | 3/11 = 0.27 | 1.1 |
| San Francisco | 3 | 3/11 = 0.27 | 0.9 |
| Boston | 2 | 2/11 = 0.18 | 0.0 |
| Miami | 2 | 2/11 = 0.18 | 0.0 |
Boston and Miami have betweenness 0: each connects only to cities already linked to each other, so no shortest path needs to pass through them. Kansas City is both well connected and a hub that many shortest routes cross.
9. Dijkstra's shortest-path algorithm
For a weighted graph with non-negative weights, the shortest path is the one with the lowest total weight, not the fewest edges. Dijkstra's algorithm finds the cheapest route from one start vertex to every other vertex.
- Give the start vertex cost 0 and every other vertex cost ∞.
- Visit the unvisited vertex with the cheapest known cost from the start.
- From it, update each neighbour: if (cost to here + edge weight) beats the neighbour's current cost, record the new cost and remember “via” this vertex.
- Repeat 2–3 until all reachable vertices are visited.
In short: visit the vertex with the cheapest path from the start, and update the cheapest fares from there. A vertex is final once visited, which is why negative weights break the method.
Table from your notes (recomputed)
| Step | B | C | D | E |
|---|---|---|---|---|
| Visit A (cheapest unvisited, cost 0). | 50 via A | ∞ | 80 via A | ∞ |
| Visit B (cheapest unvisited, cost 50). | 50 via A | 110 via B | 80 via A | ∞ |
| Visit D (cheapest unvisited, cost 80). | 50 via A | 100 via D | 80 via A | 150 via D |
| Visit C (cheapest unvisited, cost 100). | 50 via A | 100 via D | 80 via A | 140 via C |
| Visit E (cheapest unvisited, cost 140). | 50 via A | 100 via D | 80 via A | 140 via C |
Complexity: O(V²) with a plain array; O((V + E) log V) with a priority queue (heap).
10. Directed acyclic graphs and topological sort
A DAG is a directed graph with no cycles. It naturally models dependencies: course prerequisites, build steps, task schedules. A topological sort lists the vertices so that for every edge u→v, u appears before v. It exists only for DAGs (a cycle would make some vertex its own prerequisite).
Algorithm (as in your notes)
- Compute the in-degree (number of incoming edges) of every vertex.
- Take all vertices with in-degree 0, sort them, and enqueue them. Remove them from the graph, which lowers the in-degree of their successors.
- Repeat step 2 until the graph is empty. (If vertices remain but none has in-degree 0, there is a cycle.)
- Dequeue the vertices and print them.
| Round | In-degrees of remaining vertices | Removed (in-degree 0) |
|---|---|---|
| 1 | A=0, B=1, C=1, D=2, E=1, F=2 | A |
| 2 | B=0, C=0, D=2, E=1, F=2 | B, C |
| 3 | D=0, E=0, F=2 | D, E |
| 4 | F=0 | F |
11. Minimum spanning tree (Kruskal and Prim)
A spanning tree of a connected, weighted, undirected graph connects all V vertices using exactly V−1 edges and no cycles. The minimum spanning tree (MST) is the one with the smallest total weight, e.g. the cheapest cable network linking every office. Two greedy algorithms build it.
Kruskal: pick the globally cheapest edges
- Sort all edges by increasing weight.
- Start with the vertices and no edges.
- Take the next edge; add it unless it forms a cycle.
- Stop when all vertices are connected.
Prim: grow one tree outward
- Start with any vertex in the “visited” set.
- Pick the cheapest edge from a visited vertex to an unvisited one.
- Add that edge and vertex.
- Stop when all vertices are visited.
The example is the 8-vertex graph from your MST worksheet. Both algorithms reach the same tree here, with total weight 1.99. In general, ties can give different trees of equal weight.
Kruskal's edge order
| Edge | Weight | Decision | Running total |
|---|---|---|---|
| 7–3 | 0.13 | add | 0.13 |
| 2–0 | 0.14 | add | 0.27 |
| 4–1 | 0.30 | add | 0.57 |
| 2–3 | 0.31 | add | 0.88 |
| 1–7 | 0.31 | add | 1.19 |
| 1–3 | 0.35 | skip (cycle) | |
| 5–6 | 0.37 | add | 1.56 |
| 3–5 | 0.43 | add | 1.99 |
Prim's choices (start 0)
| Edge added | Weight | Running total |
|---|---|---|
| 0–2 | 0.14 | 0.14 |
| 2–3 | 0.31 | 0.45 |
| 3–7 | 0.13 | 0.58 |
| 7–1 | 0.31 | 0.89 |
| 1–4 | 0.30 | 1.19 |
| 3–5 | 0.43 | 1.62 |
| 5–6 | 0.37 | 1.99 |
| Kruskal | Prim | |
|---|---|---|
| Strategy | add cheapest edge anywhere that makes no cycle | add cheapest edge leaving the current tree |
| Needs | sorted edge list + cycle check (union-find) | priority queue of frontier edges |
| Time | O(E log E) | O(E log V) with a heap |
| Tends to suit | sparse graphs | dense graphs |
12. Modelling a puzzle as a graph
Graphs are not only for maps. Any problem made of states and moves becomes a graph: each state is a vertex, each legal move is an edge. Solving the puzzle then means finding a path from the start state to the goal state, which is exactly what BFS does.
Farmer, Wolf, Goat, Cabbage. The farmer must ferry all three across a river in a boat that holds him plus one item. Left alone, the wolf eats the goat and the goat eats the cabbage. Each vertex shows (left bank | right bank), with F = farmer, W = wolf, G = goat, C = cabbage. Unsafe states are simply left out of the graph.
Reading the path: take the goat across, return alone, take the wolf (or cabbage), bring the goat back, take the cabbage (or wolf), return alone, take the goat. The diamond is where the two solutions split and rejoin.
13. Cheat sheet
| Problem | Algorithm | Key idea | Time |
|---|---|---|---|
| Visit everything, go deep | DFS | stack, backtrack | O(V + E) |
| Fewest edges / level order | BFS | queue, rings outward | O(V + E) |
| Cheapest path (weights ≥ 0) | Dijkstra | always finalise the cheapest unvisited vertex | O((V+E) log V) |
| Order tasks with dependencies | Topological sort | repeatedly remove in-degree-0 vertices | O(V + E) |
| Cheapest network joining all vertices | Kruskal / Prim | greedy, never form a cycle | O(E log E) / O(E log V) |
| 2-colourability | BFS colouring | alternate colours level by level | O(V + E) |
| Concept | One-line summary |
|---|---|
| Degree | number of edges at a vertex (in/out for directed graphs) |
| Eulerian cycle | every edge once; needs all degrees even |
| Hamiltonian cycle | every vertex once; no simple test |
| Adjacency matrix / list | O(V²) space, fast edge test / O(V+E) space, fast neighbour scan |
| Edge density | edges ÷ V(V−1)/2 |
| Node connectivity | fewest vertices whose removal disconnects the graph |
| Algebraic connectivity | second-smallest Laplacian eigenvalue; > 0 iff connected |
| Degree / betweenness centrality | how many neighbours / how many shortest paths pass through |
| Clique | complete subgraph; clique number = size of the largest |
Time complexities and the Euler-path and bipartite tests are standard results added to round out your notes. Everything else comes from your lecture notes and worksheets.