Coding Interview Patterns Module 21 – Topological sort and DAGs
Smallest and unique topological orders
Find the smallest topological order with a min-heap, test whether only one order exists, and decide sequence reconstruction from partial rankings.
What you will learn
- Compute the lexicographically smallest topological order with a min-heap
- Check whether exactly one topological order exists
- Decide whether a set of partial rankings forces one full ranking
- Prove that taking the smallest ready node is always safe
- Distinguish the smallest order from the order that puts small numbers early
Before you start
What you’ll learn · Before you start
What you will learn
- Compute the lexicographically smallest topological order with a min-heap
- Check whether exactly one topological order exists
- Decide whether a set of partial rankings forces one full ranking
- Prove that taking the smallest ready node is always safe
- Distinguish the smallest order from the order that puts small numbers early
Before you start
On this page
Most directed acyclic graphs have many topological orders, so problems add a rule: return the smallest, the one that comes first when orders are compared number by number, or say whether only one order exists. Both questions turn on the moment in Kahn’s algorithm when more than one node is ready. The smallest order always takes the smallest ready node, which a min-heap hands out in O(log n) time. Unique topological orders are those in which that moment never comes: at every step exactly one node is ready.
Three problems: speakers, one order, and a ranking
Problem A: the smallest order. Speakers are numbered 0 to n − 1, and each pair (a, b) in before means that
a speaks before b. Return the order that keeps every pair and comes first number by number, or [] if none does.
Limits: up to 100,000 speakers and 200,000 pairs.
smallest_order(6, [(3, 0), (4, 0), (5, 2), (2, 1)])gives[3, 4, 0, 5, 2, 1]. Kahn’s queue gives[3, 4, 5, 0, 2, 1], which is valid but larger in the third place.- With
(1, 5)added, the pairs contain a loop:[].
Problem B: one order or several? Return the order if it is the only one, else nothing. The speakers’ pairs allow
40 orders; adding (3, 4) and (0, 5) leaves exactly one, [3, 4, 0, 5, 2, 1].
Problem C: sequence reconstruction. Referees report how some players of a tournament finished, each report a list
in finishing order. Is the ranking seq of all n players the only one that keeps every report? With seq = [0, 1, 2, 3], the reports [0, 1, 3] and [1, 2, 3] say yes; [0, 1, 3] and [0, 2, 3] say no, since [0, 2, 1, 3]
keeps both.
The speakers of problem A and who waits for whom
Text description of the diagram
The diagram shows six speakers, numbered 0 to 5, as boxes, with an arrow from a speaker to every speaker who must come after them.
- Speakers 3, 4 and 5 wait for nobody. Their boxes have double borders: at the start, any of the three may speak first.
- Speaker 0 waits for speakers 3 and 4, so arrows lead to it from both.
- Speaker 2 waits for speaker 5, and speaker 1 waits for speaker 2, so 5, 2 and 1 form a chain.
The pairs leave many orders open: three speakers can start, and the chain 5, 2, 1 can be interleaved with 3, 4 and 0 in many ways. The smallest order, compared number by number, starts with 3.
The brute force: test every order
Generate the orders of 0 to n − 1 in increasing order and test each one: the first that keeps every pair is the
smallest, and counting all that do answers whether only one exists.
"""The brute force: try every order of the speakers, in increasing order, and test each against the constraints."""
from itertools import permutations
def valid(order, before):
at = {v: i for i, v in enumerate(order)}
return all(at[a] < at[b] for a, b in before)
def first_and_count(n, before):
"""The first valid order in increasing order, how many orders were tried to find it, and how many are valid."""
first, tried, count = None, 0, 0
for order in permutations(range(n)): # permutations of 0..n-1 come out in increasing order
if first is None:
tried += 1
if valid(order, before):
count += 1
if first is None:
first = list(order)
return first, tried, count
speakers = [(3, 0), (4, 0), (5, 2), (2, 1)] # (a, b): speaker a speaks before speaker b
first, tried, count = first_and_count(6, speakers)
print(f"speakers: first valid order {first}, found after {tried} of 720 orders; {count} valid orders in all")
first, tried, count = first_and_count(6, speakers + [(3, 4), (0, 5)])
print(f"with 3 before 4 and 0 before 5 added: first {first}, {count} valid order")
print("\nTo show that only one order is valid, the brute force has to test every order:")
for n in (6, 7, 8, 9):
first, tried, count = first_and_count(n, [(i, i + 1) for i in range(n - 1)])
total = sum(1 for _ in permutations(range(n)))
print(f"a chain of {n} speakers: {count} valid order among {total:,} tested") Output
speakers: first valid order [3, 4, 0, 5, 2, 1], found after 438 of 720 orders; 40 valid orders in all with 3 before 4 and 0 before 5 added: first [3, 4, 0, 5, 2, 1], 1 valid order To show that only one order is valid, the brute force has to test every order: a chain of 6 speakers: 1 valid order among 720 tested a chain of 7 speakers: 1 valid order among 5,040 tested a chain of 8 speakers: 1 valid order among 40,320 tested a chain of 9 speakers: 1 valid order among 362,880 tested
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 all_orders.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The smallest order of the speakers turns up after 438 of the 720 orders. Proving an order unique is worse, because every order must be tested: 362,880 for a chain of 9 speakers, and 20! ≈ 2.4 × 10¹⁸ for 20, while the limits allow 100,000.
What to notice
Kahn’s algorithm may take any ready node next, and all three problems are rules about that choice:
- A min-heap of ready nodes hands out the smallest one, which gives the smallest order.
- The uniqueness test stops as soon as two nodes are ready at once, so it finds unique topological orders in one run.
- Reconstruction checks that every two neighbours in the ranking appear side by side in some report.
- Putting small numbers early is a different rule: take the largest ready node of the reversed pairs.
The smallest order with a min-heap, step by step Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why taking the smallest ready node is always safe: the proof
- When exactly one order exists, and the proof
- Sequence reconstruction, step by step
- Dry runs on the speakers and the reports
- The code in five languages
- How much work it does
- Edge cases and six bugs, found on the smallest inputs
- Variants: the largest order, two orders compared, small numbers early, counting orders
- Exercise: the smallest order
- The smallest order that keeps every pair
- A practice set with sample answers
- Check yourself
- Interview questions
- Kahn's algorithm with a min-heap and with a max-heap returns the same order. What does that tell you?
- Why does taking the smallest ready node at every step give the lexicographically smallest topological order? A colleague worries that a small choice now could force a larger number later.
- A league publishes a final ranking of 120 teams and says it follows from 400 head-to-head results, each one a winner placed above a loser. How do you check that the results force exactly this ranking, and what do you report when they do not?
- Jobs are numbered 1 to n, and some must run before others. Operations wants job 1 as early as possible, then job 2 as early as possible given that, and so on. A teammate runs Kahn's algorithm with a min-heap. Why is that wrong, and what is right?
- Key takeaways
5 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 25 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- Topological sorting of large networks (A. B. Kahn, Communications of the ACM 5(11), pages 558–562) (Association for Computing Machinery (ACM))
- topological sort (Dictionary of Algorithms and Data Structures) (National Institute of Standards and Technology (NIST))
- heapq: heap queue algorithm (The Python Standard Library) (Python Software Foundation)
- PriorityQueue (Java SE 25 and JDK 25 API) (Oracle)
- std::priority_queue (cppreference.com) (cppreference.com)
- Package container/heap (Go standard library) (The Go Authors)
- Standard built-in objects (JavaScript reference) (MDN Web Docs)
- List (Java SE 25 and JDK 25 API): removeLast() (Oracle)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress