Topological Sort

Context: FIT2004_MOC · linearise a DAG so every edge points forward — two routes, both reductions of traversal Parent Framework: Directed Acyclic Graph (DAG)

Quick Revision

  • 🎯 Objective: output a permutation of in which precedes for every edge ➔ a legal execution order for the dependencies.
  • 📦 Core Components: Kahn’s ➔ repeatedly emit an in-degree- vertex | modified DFS ➔ push each vertex on finish, then read the stack top-down.
  • ⚠️ Key Constraint: the answer is not unique — incomparable vertices may come in either order, so a marker’s differing answer is not a contradiction.

📝 How It Works

1. What “Valid” Means

  • The obligation ➔ for every edge , position of position of . Nothing else is constrained.
  • Incomparable vertices are free ➔ with no edge between and , both orders are legal ➔ many valid outputs per DAG.
  • Checking an answer ➔ scan the edge list once and test each edge against the emitted positions; , and the fastest way to mark your own trace.
  • Cycle nothing ➔ if fewer than vertices are emitted, the leftovers form a cycle and no valid order exists.

2. Kahn’s Algorithm — peel the sources

  • Idea ➔ a vertex with no incoming edge owes nothing, so it can go next; emit it, delete its out-edges, and whatever that frees becomes emittable.
  • Bookkeeping ➔ one incoming_edges count per vertex, built in ; a process collection holding every currently-zero vertex.
  • Queue or stack ➔ process may be either; the rule fixes only which vertices are eligible, not which eligible one you pick ➔ different choices give different valid orders.
  • Decrement, do not rescan ➔ each edge is decremented once, which keeps the total at rather than .
  • The queue reads out uniqueness ➔ if process never holds more than one vertex, no step ever had a choice ⟹ the order is unique; two eligible vertices at any step means a second valid order exists ➔ §5 turns that into a Hamiltonian-path test.

3. Modified DFS — push on finish

  • The observation ➔ raw DFS visit order is not a topological order: after hitting a dead end it returns to an earlier vertex that must appear early. On the lecture DAG, DFS visits , which is invalid.
  • The repair ➔ push onto a stack only after every out-neighbour has finished (post-order); the topological order is the stack popped, i.e. the reverse of the push order.
  • Why it works ➔ finishes after all of its descendants, so its push is deeper in the stack than theirs ➔ on popping, emerges first.

4. What a Topological Order Certifies — Reachability Bounds a Position (applied P11)

  • The reframing ➔ questions of the form is guaranteed to be among the first ? are not about one order but about every order, so they must be answered from structure, never by computing one topological sort and reading off positions.
  • Two reachability counts per vertex ➔ vertices reachable from (its dependants — they must come after it) · vertices that reach (its superiors — they must come before it). Everything else may fall on either side.
  • Guaranteed in the first ➔ at least vertices are pinned after , so its position is at most in every order.
  • Guaranteed not in the first ➔ at least vertices are pinned before , so its position is at least in every order.
  • Computing them ➔ is one DFS from on ; is one DFS from on with every edge reversed. Two traversals per vertex ⟹ .
  • Why not “the first of a topological order” ➔ the order is not unique, so an incomparable vertex can sit inside or outside the prefix depending on the tie-break; only vertices pinned by reachability are guaranteed either way ➔ §1.

5. Hamiltonian Path in a DAG — Unique Order or None (applied P12, advanced)

  • The claim chain ➔ a Hamiltonian path exists in a DAG iff the graph has a unique topological order, and then the path is that order.
  • A Hamiltonian path must be a topological order ➔ if the path ever ran to a vertex that must come earlier, that edge plus the path segment would close a directed cycle, impossible in a DAG.
  • Hamiltonian path unique order ➔ the path makes every pair of vertices comparable (one is an ancestor of the other), so no two vertices are free to swap ⟹ exactly one valid order.
  • Unique order Hamiltonian path ➔ if some consecutive pair in the order were not adjacent, swapping them would break no dependency and yield a second valid order, contradicting uniqueness; so every consecutive pair is joined by an edge, which is a path through all vertices.
  • The algorithm ➔ compute any topological order, then scan it once testing that each consecutive pair is adjacent. , and the test doubles as the uniqueness certificate.
  • Why this is not NP-hard here ➔ Hamiltonian path is NP-hard on general graphs; acyclicity is the whole gift, because it forces the candidate path to be a topological order and there is at most one to check.
  • The cheap certificate ➔ uniqueness need not be re-derived — Kahn’s process collection never exceeding size is the proof (§2), so run Kahn’s when the question wants the path itself.

⚙️ Core Implementation

🔹 Kahn’s algorithm

🔹 Modified DFS

⚖️ Complexity

VariantTimeAuxiliary spaceCycle detectionOrder it tends to produce
Kahn’s — counts processfree: emitted ⟹ cyclesources first; natural for “what can I start now”
Modified DFS — stack recursion depthneeds an extra in-progress mark to spot a back edgesinks first, then reversed
Brute force (try permutations)—never do this
  • Where the comes from ➔ every vertex enters and leaves the working collection exactly once (), and every edge is inspected exactly once () — identical accounting to DFS.
  • Recursion is not free ➔ the DFS variant’s stack depth is in the worst case (a path graph), so it is not in-place (Analysing Recursive Algorithms (Time and Auxiliary Space)).

⚖️ Core Decision Matrix

NeedPickWhy
Detect a cycle as a by-productKahn’sthe count of emitted vertices is the certificate — no extra state
Already have a recursive DFS in the codebasemodified DFSone line added: push on finish
Iterative code required (deep graph, no recursion)Kahn’sexplicit collection, no call stack to overflow
”Which tasks can run in parallel right now”Kahn’seach round of zero-in-degree vertices is one parallel layer

When It Flips: the two agree on cost, so the choice is driven by what else you need — a cycle certificate and layering favour Kahn's; an existing DFS traversal favours the post-order push.

📊 Exam Execution Trace & Applied Exercises

Lecture DAG: , , , , , . In-degrees , , , , .

Manual Execution Trace

Kahn’s, process used as a stack (pop() from the end).

StepPoppedDecremented toprocess aftersorted_list
0——
✅, ✅
, ✅
✅
—

Final Extracted Output: · all vertices emitted ⟹ the graph is a DAG.

Applied Exercise

Problem: which of is not a valid topological sort?

Final Extracted Output: option 4 is invalid; – each satisfy all six constraints, differing only in the order of incomparable pairs — exactly the non-uniqueness the definition permits.

Applied Exercise

Problem: DFS from (neighbours in alphabetical order) — give the visit order and the topological order, and explain why they differ.

Final Extracted Output: the visit order is invalid ( precedes , yet exists); the pop order is valid. Pre-order records when we arrived, post-order when everything downstream was settled — only the second respects the dependencies.

⚠️ Common Mistakes

  • 💡 Emitting the DFS visit order ➔ the single highest-frequency error; DFS pre-order has no ordering guarantee, only the reversed post-order does.
  • 💡 Claiming “the” topological sort ➔ say a topological sort, and if the question demands determinism, state your tie-break rule before tracing.
  • 💡 Reading a guarantee off one topological order ➔ positions in a single order are an artefact of the tie-break; “guaranteed in every order” is a reachability question ( / ), not a positional one.
  • 💡 Reporting success on a cyclic graph ➔ Kahn’s terminates happily with a short sorted_list; the check is len(sorted_list) == V.

🧠 Active Recall