🚀 Alguni Start learning

Unit 26: Graphs

Maps, friends and finding paths.

Unit 26 of 31 in Python for kids. Its 5 lessons are Dots and Lines, Spreading Out, Going Deep, Finding a Way Through and Graph Master — below is everything each one explains, and a question or two from it to try.

Every sample on this page was run through real Python before it shipped, and prints exactly what it says it prints.

This unit opens with a fortnight’s trial of everything — no card needed — or with a family plan, bought in the iPhone app. The first two units of every track are free for ever. Try it in the app.

🕸️ Dots and Lines

When a tree is not enough

A tree branches downwards and never joins back up. A graph lets anything connect to anything — towns joined by roads, people who know each other, web pages that link. The dots are nodes and the lines are edges.

Python

roads = {
    "home": ["shop", "park"],
    "shop": ["home"],
    "park": ["home"],
}
print(roads["home"])
print(len(roads))

It prints

['shop', 'park']
3

Both ways, or only one

If the connection goes both ways, put each in the other's list. A one-way graph (like following someone online) only lists it once.

Python

friends = {
    "Ada": ["Sam"],
    "Sam": ["Ada"],
}
print("Sam" in friends["Ada"])
print("Ada" in friends["Sam"])

It prints

True
True

Counting connections

How many edges a node has is its degree — how busy that place is.

Python

graph = {
    "a": ["b", "c"],
    "b": ["a"],
    "c": ["a"],
}
for node in graph:
    print(node, len(graph[node]))

It prints

a 2
b 1
c 1

Try it yourself

What is an edge?

  • The outside of the graph
  • A connection between two nodes
  • The last node
  • A value stored in a node

What does this print?

Python

g = {"x": ["y", "z"], "y": [], "z": ["y"]}
print(len(g["x"]))
print(g["y"])

Answer them in the app

🌊 Spreading Out

Visiting everything, nearest first

Breadth-first search spreads out like a ripple: all the places one step away, then all those two steps away. It uses a queue — which is why you met one in unit 21.

Python

from collections import deque

graph = {
    "a": ["b", "c"],
    "b": ["d"],
    "c": [],
    "d": [],
}

def bfs(start):
    seen = [start]
    queue = deque([start])
    while queue:
        node = queue.popleft()
        print(node)
        for neighbour in graph[node]:
            if neighbour not in seen:
                seen.append(neighbour)
                queue.append(neighbour)

bfs("a")

It prints

a
b
c
d

Why you must remember where you have been

Graphs can loop back on themselves. Without the seen list you would go round for ever — this is the bug everyone writes once.

Python

graph = {"a": ["b"], "b": ["a"]}
seen = []
queue = ["a"]
steps = 0
while queue and steps < 10:
    node = queue.pop(0)
    if node in seen:
        continue
    seen.append(node)
    for n in graph[node]:
        queue.append(n)
    steps = steps + 1
print(seen)

It prints

['a', 'b']

Counting how far away things are

Because BFS reaches everything by the shortest route first, you can record the distance as you go.

Python

from collections import deque

graph = {"a": ["b"], "b": ["c"], "c": []}

distance = {"a": 0}
queue = deque(["a"])
while queue:
    node = queue.popleft()
    for n in graph[node]:
        if n not in distance:
            distance[n] = distance[node] + 1
            queue.append(n)

print(distance)

It prints

{'a': 0, 'b': 1, 'c': 2}

Try it yourself

What does BFS use to decide what to visit next?

  • A stack
  • A queue
  • A dictionary
  • Random choice

Answer it in the app

🕳️ Going Deep

One word different

Depth-first search follows one path as far as it goes before backing up. The code is BFS with the queue swapped for a stack — pop instead of popleft. That is genuinely the only change.

Python

graph = {
    "a": ["b", "c"],
    "b": ["d"],
    "c": [],
    "d": [],
}

def dfs(start):
    seen = []
    stack = [start]
    while stack:
        node = stack.pop()
        if node in seen:
            continue
        seen.append(node)
        print(node)
        for neighbour in graph[node]:
            stack.append(neighbour)

dfs("a")

It prints

a
c
b
d

Or let recursion hold the stack

Recursion already keeps track of where to go back to, so DFS is often written without a stack at all.

Python

graph = {
    "a": ["b", "c"],
    "b": ["d"],
    "c": [],
    "d": [],
}
seen = []

def dfs(node):
    if node in seen:
        return
    seen.append(node)
    print(node)
    for neighbour in graph[node]:
        dfs(neighbour)

dfs("a")

It prints

a
b
d
c

Try it yourself

What is the real difference between BFS and DFS?

  • BFS is always faster
  • Which container holds the waiting nodes — a queue for BFS, a stack for DFS
  • DFS visits fewer nodes
  • BFS cannot handle loops

You want the fewest steps between two places. Which do you use?

  • DFS
  • BFS
  • Either
  • Neither

Answer them in the app

🗺️ Finding a Way Through

Can I get there at all?

Once you can visit everything reachable, "is there a route?" is just asking whether the place you want turned up.

Python

graph = {"a": ["b"], "b": ["c"], "c": [], "z": []}

def can_reach(start, target):
    seen = []
    stack = [start]
    while stack:
        node = stack.pop()
        if node == target:
            return True
        if node in seen:
            continue
        seen.append(node)
        for n in graph[node]:
            stack.append(n)
    return False

print(can_reach("a", "c"))
print(can_reach("a", "z"))

It prints

True
False

Remembering the way back

To get the actual route, record which node you *came from*. Then walk that trail backwards from the finish.

Python

from collections import deque

graph = {"a": ["b"], "b": ["c"], "c": []}

came_from = {"a": None}
queue = deque(["a"])
while queue:
    node = queue.popleft()
    for n in graph[node]:
        if n not in came_from:
            came_from[n] = node
            queue.append(n)

path = []
at = "c"
while at is not None:
    path.append(at)
    at = came_from[at]
print(path[::-1])

It prints

['a', 'b', 'c']

Try it yourself

What does this print?

Python

path = ["c", "b", "a"]
print(path[::-1])

Why does BFS give the shortest path?

  • It tries every path and picks the best
  • It reaches each node by the fewest steps first, so the first way it finds is the shortest
  • It sorts the nodes
  • It does not

Answer them in the app

🏆 Graph Master

Try it yourself

What does this print?

Python

g = {"a": ["b", "c"], "b": [], "c": []}
total = 0
for node in g:
    total = total + len(g[node])
print(total)

Answer it in the app