Chaya·Edu EDU.CHAYA.DEV
AI 230 · Notes 11
Sessions 20–21 · Nov 10 & 12, 2026
Notes 11 · Week 11 · Sessions 20–21

Graphs: Maps, Mazes, and Exploring Without Getting Lost

AI 230 · Introduction to Algorithms Prof. Yair Chaya Long Island University, Brooklyn Tuesday & Thursday, November 10 & 12, 2026 · 4:00–5:50 PM

Last week closed on a warning: everything the notebook touched shrank cleanly — peel a letter, cut a branch, and it never came back. Tonight we walk into the country where that stops being true. The subway map, your social network, your degree's prerequisite chart, the internet itself — they are all graphs: dots connected by links, where the links can loop back around. You can walk in circles. So before graphs give us anything, we have to learn the explorer's first skill: how to visit everything, once, without getting lost.

Two live demonstrations · marked ● in the contents

The ripple vs. the maze-walker: one map, thirteen dots, two completely different ways to explore it — tap any dot and watch the queue and the stack do their work, or ask for the shortest route between any two dots. And the wardrobe game: get a student dressed without ever putting shoes on before socks — then add one cursed rule and watch the whole morning become impossible. Every number is computed live.

Session planHow we will spend the two sessions

Tuesday, November 10 — Session 20

0:00
The world stops shrinking politely: what a graph is (§1)
0:20
How a computer holds a map: friends list vs. checkbox table (§2)
0:45
The ripple: breadth-first search, and why first touch = shortest (§3)
1:15
The ripple vs. the maze-walker — lab, part 1 (§4)
1:35
Project 3 check-in — blockers round

Thursday, November 12 — Session 21

0:00
The maze-walker: depth-first search (§4)
0:20
Lab, part 2: racing the two explorers from the same dot (§4)
0:40
Getting dressed in the right order: topological sort (§5)
1:05
The wardrobe game — lab (§5)
1:20
Islands: who can reach whom (§6)
1:40
Project 3 work time — bring your blockers (§7)

Section 1The world stops shrinking politely

You already live on graphs. The subway map: stations connected by track. Your social network: people connected by friendships. Your degree: courses connected by "take this first" arrows. The internet: pages connected by hyperlinks, routers connected by cables. In every one of them, the object is the same two-ingredient thing:

Definition — the only one tonight needs

A graph is a set of dots with links between some of them. The official names — a dot is a vertex, a link is an edge — are worth hearing once, because every book and every library uses them. We will mostly say dots and links, because that is what they are.

So what's new? Trees from last week were graphs too — dots and links, with one promise attached: no loops. Cut a branch and it fell away forever; every step down the org chart made the problem smaller. Graphs make no such promise. Subway lines form circles on purpose. Your friend's friend's friend can be you. And that one missing promise changes everything about how you explore:

last week: a tree — walk down and what's left always shrinks. No loops. this week: a graph — the gold links form a loop. A careless walker circles it until the heat death of the universe.
The one-promise difference. A tree shrinks under you as you walk. A graph can hand you the same dot again and again — so exploring needs discipline: a way to remember where you've been, and a plan for what to try next. Those two things are tonight's entire subject.

Here is the good news hiding in that picture: the discipline turns out to be small. Two exploration strategies — one patient, one bold — plus a pocketful of breadcrumbs, and every graph on Earth opens up. Everything else this week (ordering tasks, finding islands) falls out of those two strategies almost for free.

Section 2How a computer holds a map

Before exploring a graph, a program has to hold one, and there are exactly two classic ways. Both answer the same question — "who is linked to whom?" — with opposite trade-offs. Take six people, A through F, with seven friendships:

A B C D E F A: B C B: A D C: A D E D: B C F E: C F F: D E ABCDEF ABCDEF the picture the friends list the checkbox table
One graph, three costumes. The picture is for humans. The friends list stores, for each dot, exactly who it links to — nothing more. The checkbox table has a row and a column for every dot, with a mark where a link exists — 36 boxes to record 7 friendships, and most boxes empty. (The textbook names: adjacency list and adjacency matrix.)

In Python, the friends list is a dictionary of lists, and that's the entire data structure this week runs on:

friends = {
    "A": ["B", "C"],
    "B": ["A", "D"],
    "C": ["A", "D", "E"],
    "D": ["B", "C", "F"],
    "E": ["C", "F"],
    "F": ["D", "E"],
}

Which costume should you reach for? One honest sentence each. The friends list is lean: it stores only the links that exist, and real-world graphs are overwhelmingly sparse — two million users with 200 friends each is 400 million list entries, while the checkbox table for the same network is four trillion boxes, almost all of them recording the non-news that two strangers aren't friends. The table's one superpower is the instant lookup: "are A and D linked?" is one peek at one box, where the friends list has to skim A's whole list. Sparse graph, walking along links (which is everything we do tonight): friends list. Small dense graph, hammering "are these two linked?" questions: table. That's the whole buying guide.

Check yourself — Your app has 2,000,000 users averaging 200 friendships each. A teammate proposes the checkbox table "because lookups are instant." What do you say?

That the instant lookup costs 2,000,000 × 2,000,000 = 4 trillion checkboxes — roughly 4 terabytes at a byte apiece — to store what the friends list holds in about 400 million entries, ten thousand times less. The table pays for every link that could exist; the list pays only for links that do. At 200 friends per user this network is about 0.01% full, so the table is 99.99% stored silence. (If the graph were small and dense — say, 100 dots nearly all pairwise linked — the teammate would be right. Always ask how full the table would be.)

Section 3The ripple: breadth-first search

Now, exploring. Strategy one is the patient one. Drop a stone in a pond where you're standing. The ripple reaches everything 1 link away first. Then everything 2 links away. Then 3. It never skips ahead — water doesn't tunnel. Computer scientists call this breadth-first search, BFS; we'll call it the ripple.

start The ripple's promise, whole: to be standing on ring 3, the water had to come through ring 2 first — there are no tunnels. A dot 3 links out can't be reached in 2, or it would be ON ring 2. So the FIRST time the ripple touches a dot, it arrived by a shortest possible route — counted in links. Free gift, every time.
Why you should believe BFS finds shortest paths, in one picture. Each ring is "all dots exactly that many links out," and the ripple finishes a ring before starting the next. When it first reaches you, the number of rings crossed is the fewest links possible — that's what the rings mean.

To run a ripple on a computer you need two pieces of bookkeeping, and both are household objects:

from collections import deque

def ripple(friends, start):
    """Visit everything reachable from start, nearest rings first."""
    line = deque([start])              # the line at the door
    crumbs = {start}                   # breadcrumbs: already seen
    while line:
        person = line.popleft()        # serve the front — first come, first served
        print(person)
        for buddy in friends[person]:
            if buddy not in crumbs:  # no breadcrumb yet?
                crumbs.add(buddy)
                line.append(buddy)     # back of the line

Ten lines, and two of them are the whole story: popleft (patience — serve the nearest first) and crumbs (discipline — never twice). Want the actual shortest route, not just its length? When a buddy gets in line, jot down who put them there; walking those jottings backward from the destination spells out the route. You've seen that move twice already — it's the walk-back from the DP weeks, wearing hiking boots.

Check yourself — Delete the crumbs set from the code above and run it on the §2 friendship graph. What actually happens, and on which dots?

It never finishes. A gets in line, A visits B, B puts A back in line (B's friends list contains A), A puts B back, and the line grows forever — the printout is an endless churn of the same names. Any loop does it: A–B–D–C–A is a merry-go-round with no exit. This is §1's warning made concrete, and it's why the breadcrumb set isn't an optimization — it's the difference between an algorithm and a wandering ghost. (Week 1 would say: finiteness is part of the definition.)

Section 4The maze-walker: depth-first search

Strategy two is the bold one. Forget rings — plunge. At every junction, take the first unexplored hallway and keep going deeper until you hit a dead end (no unvisited neighbors). Then back up — only as far as the last junction that still has an untried hallway — and plunge again. It's exactly how you solve a hedge maze with one hand on the wall. The official name is depth-first search, DFS; we'll call it the maze-walker.

Its bookkeeping is the mirror image of the ripple's. Instead of a line at the door, the maze-walker keeps the trail of forks behind it — the junctions it passed on the way in, newest on top, so "back up" means "return to the most recent fork." That last-in-first-out object is a stack (Week 6 again). And here's the lovely part: recursion keeps that trail for you, for free. Every recursive call is a fork remembered; every return is backing up one fork. You watched the call stack do precisely this in Notes 01's recursion demo:

def maze_walk(friends, person, crumbs=None):
    """Go deep. When stuck, back up to the last fork and try the next hallway."""
    if crumbs is None:
        crumbs = set()
    crumbs.add(person)
    print(person)
    for buddy in friends[person]:
        if buddy not in crumbs:
            maze_walk(friends, buddy, crumbs)   # the call stack IS the trail of forks

Same breadcrumbs, same "visit everything once" guarantee — different order, and the order is the whole personality. When does each win?

Choosing your explorer — the whole rule
  • The ripple (BFS) when the question is about nearness: shortest route, fewest hops, closest anything. Its ring order is a promise no other order makes.
  • The maze-walker (DFS) when the question is "explore everything" or "understand the structure": solving mazes, listing every file in nested folders, and — in §5's close cousin — sniffing out loops. Going deep is cheap on memory when graphs are long and stringy, and recursion makes the code almost insultingly short.
  • Cost: identical, and honest. Both explorers touch each dot once (breadcrumbs) and peek down each hallway once from each end. Week 2 language: time grows like dots + links. You cannot do better — you can't claim to have explored a building without touching every room and every hallway.

Now race them. Thirteen dots, plenty of loops, and — notice — a little triangle in the corner that no link reaches. Tap a mode, then tap any dot:

Demonstration 1The ripple vs. the maze-walker — one map, two explorers
Mode
A B C D E F G H I J K L M
rings: start 1 link 2 links 3 links 4+ links · gold numbers = visit order
The line at the door
empty
Dots touched
—
Rings (links out)
—

Some things to try:

  1. Ripple from A and read the colors: B and E light up as ring 1, then C and F, then D, G, I, then H and J — the visit numbers never jump ahead of the rings. That picture is the shortest-path argument.
  2. Maze-walk from A and watch it bolt: A, B, C, D, H, J — six dots deep before it ever looks back, the trail of forks stacking up behind it. Same thirteen dots by the end (an island is an island either way), order entirely different.
  3. Switch to Shortest route, tap E, then H. Four links, lit in green. Try to beat it by eye. Then tap A, then K — and meet §6 early.
  4. Ripple from K. Three dots and done: the mainland might as well not exist. Keep that feeling — it's the whole of §6.
Check yourself — Which explorer, and why: (a) "fewest subway stops from here to Yankee Stadium"; (b) "list every file inside this folder and all its nested subfolders"; (c) "is there any path through this maze at all?"

(a) The ripple — "fewest stops" is exactly the ring number, and only BFS promises it. (b) The maze-walker — folders nest like a maze's corridors, and the recursive code writes itself (it's how your file explorer actually does it). (c) Either. "Is there a path" only asks whether the exit gets a breadcrumb, and both explorers reach precisely the same dots — they differ in order, never in territory. If you additionally wanted the shortest way out, (c) becomes (a): take the ripple.

Section 5Getting dressed in the right order

New kind of graph, new superpower. So far our links had no direction — a friendship points both ways. But "socks before shoes" points one way. A graph whose links are arrows is a directed graph, and the everyday version is any to-do list with dependencies: course prerequisites, build systems, recipes, and the oldest scheduling problem in the world — getting dressed.

socks underwear shirt pants shoes belt jacket 1 2 3 4 5 6 7 an arrow means "me first" · gold numbers: one valid order — socks, underwear, shirt, pants, shoes, belt, jacket. Others work too.
The getting-dressed graph. A line-up of all the tasks where every arrow points forward is called a topological order — "an order that respects the arrows." Note what the picture quietly requires: the arrows must have no loop. Nothing here says "jacket before underwear," so a valid morning exists.

How do you find such an order? The method you already use every morning, stated out loud:

The method, whole (three sentences)

Look for any task with no remaining prerequisites — nothing left to wait for. Do it, cross it off, which may free up new tasks. Repeat until everything is crossed off — or until you're stuck. (The literature calls this Kahn's algorithm; your morning routine predates the name by some decades.)

And the sting in the tail is the best part. When can this get stuck — tasks remaining, but none of them doable? Only when every remaining task waits on another remaining task, round and round: a loop in the arrows. "A requires B, B requires C, C requires A" — a degree where two courses are each other's prerequisite. No valid order exists, not because we searched badly, but because the demand is self-contradictory. So the dressing method is secretly two tools in one: it produces an order when one exists, and its failure is a proof that a cycle exists. When npm, or make, or your university's registrar prints circular dependency detected — this is the tool that said it, getting stuck on purpose.

Try the morning routine yourself — then curse it:

Demonstration 2The wardrobe game — find a valid order, or prove there isn't one
The rules
underwear before pants · pants before shoes & belt · socks before shoes · shirt before belt & jacket · belt before jacket
The wardrobe — tap what goes on next (highlighted = nothing left to wait for)
You are wearing, in order
nothing yet

Two honest notes the demo makes vivid. First, many valid orders usually exist — the wardrobe game will tell you exactly how many its rules allow, and any of them is a full-credit answer; "the" topological order is almost never unique. Second, when the cursed arrow is in place, notice where you get stuck: not immediately, but after the free items run out — which is exactly how real circular-dependency bugs feel. Everything builds fine until suddenly nothing does.

The dressing method in Python — eight lines, stuck-detection included
def get_dressed(tasks, needs):      # needs[t] = set of t's prerequisites
    done = []
    while len(done) < len(tasks):
        ready = [t for t in tasks
                 if t not in done and needs[t] <= set(done)]
        if not ready:
            return None              # stuck with tasks left = there's a loop
        done.append(ready[0])         # anything ready is fine — hence many valid orders
    return done

This is the plain-words method, transcribed. (Production versions keep a count of unmet prerequisites per task so they never rescan the whole list — same idea, faster bookkeeping, and the cost lands back at our familiar "dots + links.")

Check yourself — Four tasks: buy beans (needs nothing), grind beans (needs buy), brew coffee (needs grind), toast bread (needs nothing). How many valid orders are there?

Four. The coffee chain buy → grind → brew is rigid, but the toast has no arrows touching it, so it can slot into any of the four gaps: before buy, after buy, after grind, or after brew. One graph, four perfectly correct mornings — which is the "many valid orders" point in miniature. (And if toast had needed brew, say, the count would drop to one. Every arrow you add removes freedom.)

Section 6Islands: who can reach whom

One more gift, and it's almost free. In Demonstration 1, rippling from A touched ten dots and rippling from K touched three — and no run ever touched all thirteen. The map isn't one territory; it's two islands. Dots A–J can all reach each other; K, L, M can all reach each other; and no link bridges the gap. Each island is called a connected component, and finding them all takes exactly one idea:

Counting islands — the whole method

Start anywhere; run a ripple (or a maze-walk — territory is the same either way). Everything it touches is island number 1. Now scan for any dot with no breadcrumb; explore from there — that's island 2. Repeat until every dot has a crumb. The crumbs are shared across all the runs, so each dot and each link still gets touched once: the whole census costs one sweep, dots + links.

def count_islands(friends):
    crumbs, islands = set(), 0
    for person in friends:
        if person not in crumbs:
            islands += 1                       # an unseen dot = a new island
            ripple_into(friends, person, crumbs)   # §3's ripple, sharing the crumb set
    return islands

Why care? Because "are these two things connected at all?" is a question the real world asks constantly. Friend circles that never overlap. A power-grid failure that splits a region into cells — which neighborhoods lost contact with the plant is a components question. "Is this maze even solvable" is "do start and exit share an island." Network engineers monitoring whether an outage has partitioned the internet are counting components on a very large, very expensive graph. The algorithm is the one above, every time.

One honest sentence about one-way streets: when the links are arrows, "I can reach you" stops implying "you can reach me" (every follower of a celebrity knows this), so "island" needs more care to define there — a story for a later course; this week's two-way islands are the workhorse case.

Check yourself — You ripple from one dot of a 50-dot network and touch all 50. How many islands? You try again on a different network and touch 49. Now what do you know, and what do you do?

Touching all 50 means exactly one island — everything reaches everything, no further runs needed. Touching 49 means at least two: one dot has no crumb, so no chain of links connects it to your start. Do what the census says: explore from the crumbless dot. If it touches only itself, the answer is two islands (a 49 and a 1 — a hermit); it can't touch more than that one dot without contradicting the first run. Notice you never needed to "search for the gap" — the breadcrumbs found it for you.

Section 7Before next session

To do — before Tuesday, November 17
  1. Review Notes 11. Then close the page and narrate from memory: the two explorers' bookkeeping (the line vs. the trail), why the ripple's first touch is shortest, and how the dressing method doubles as a cycle detector. If you can also say when a checkbox table beats a friends list, you own the week.
  2. Project 3 is due next week. Final stretch — bring one concrete blocker to Thursday's work time if you have one. Stuck is normal; silently stuck is the only mistake.
  3. In the demos: race both explorers from every dot on the map and watch what changes (order) and what never does (territory). Then take the cursed wardrobe and find the other dead end — there's more than one way to get stuck.
  4. Optional challenge: write ripple with the "who put you in line" jottings and print the actual shortest route between two dots — then check yourself against Demonstration 1's Shortest route mode.
  5. Optional rabbit hole: the "six degrees of separation" claim is a statement about BFS rings on the human friendship graph. Look up the Bacon number (or the Erdős number) and compute what it is in ripple language. Facebook once measured its whole graph's average: 3.57 rings.
Coming up · Notes 12 · Tuesday & Thursday, November 17 & 19

This week the links just existed. Next week they get prices — and we build the cheapest network that still connects everyone: the problem every ISP, power grid, and railroad has had to solve. Two greedy strategies that actually work (a rare luxury, as Week 8 taught you), plus the data structure that makes them almost free: union-find, the fastest island-keeper ever built. Project 3 is due that week. Sessions 22–23.