← Back to All Patterns

Topological Sort Pattern

📚 Topological Sort - Course Schedule

Dependencies: 0→1, 0→2, 1→3, 2→3

0
in: 0
1
in: 1
2
in: 1
3
in: 2

Queue (Courses with no prerequisites):

0

Course Order (Result):

No courses taken yet

Step 1: Start: Course 0 has no prerequisites (in-degree=0)

Progress: 1 / 5

Speed:

Kahn's Algorithm:

  1. Find all nodes with in-degree 0 (no dependencies)
  2. Add them to queue and result
  3. Reduce in-degree of their neighbors
  4. Repeat until queue is empty

🎯 Explain Like I'm 5...

Imagine getting dressed 👕 in the morning! You MUST put on socks 🧦 BEFORE shoes 👟, and underwear BEFORE pants! Some things have rules!

👔 Getting Dressed Rules:

  • Socks 🧦 → THEN Shoes 👟 (can't wear shoes first!)
  • Underwear 🩲 → THEN Pants 👖
  • Shirt 👕 (no rules, wear anytime!)

✅ Good Orders (Topological Sort):

  • Socks → Underwear → Shirt → Pants → Shoes ✓
  • Underwear → Socks → Pants → Shoes → Shirt ✓
  • Shirt → Socks → Shoes → Underwear → Pants ❌ (Shoes before Socks - WRONG!)

🚀 The Trick: Do the things with NO requirements first! Then cross them off and see what's next. It's like a checklist where you can only do some things after finishing others! 📋✓

When Should You Use This Pattern?

  • Problems with dependencies or prerequisites
  • Finding valid ordering of tasks
  • Detecting cycles in directed graphs
  • Course scheduling, build systems

📝 Example 1: Course Schedule

Determine if you can finish all courses given prerequisites. (Cycle detection)

Step-by-Step Thinking:

  1. Build graph and count in-degrees (incoming edges)
  2. Add all courses with 0 in-degree to queue (no prerequisites)
  3. Process each course, reduce in-degree of dependents
  4. If all courses processed, possible! If not, there's a cycle.
CourseSchedule.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import java.util.*;
public class CourseSchedule {
// Check if all courses can be finished
public static boolean canFinish(int numCourses, int[][] prerequisites) {
// Build graph and in-degree array
Map<Integer, List<Integer>> graph = new HashMap<>();
int[] inDegree = new int[numCourses];
for (int i = 0; i < numCourses; i++) {
graph.put(i, new ArrayList<>());
}
for (int[] prereq : prerequisites) {
int course = prereq[0];
int prerequisite = prereq[1];
graph.get(prerequisite).add(course);
inDegree[course]++;
}
// Queue for courses with no prerequisites
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}
int coursesCompleted = 0;
// Process courses
while (!queue.isEmpty()) {
int course = queue.poll();
coursesCompleted++;
// Reduce in-degree of dependent courses
for (int dependent : graph.get(course)) {
inDegree[dependent]--;
if (inDegree[dependent] == 0) {
queue.offer(dependent);
}
}
}
return coursesCompleted == numCourses;
}
public static void main(String[] args) {
int numCourses = 4;
int[][] prerequisites = {{1, 0}, {2, 0}, {3, 1}, {3, 2}};
System.out.println("Can finish: " + canFinish(numCourses, prerequisites));
// Output: true
// Valid order: 0 → 1 → 2 → 3
prerequisites = new int[][]{{1, 0}, {0, 1}};
System.out.println("Can finish (cycle): " + canFinish(2, prerequisites));
// Output: false (0 needs 1, 1 needs 0 - cycle!)
}
}

📝 Example 2: Course Schedule II (Find Order)

Return the ordering of courses you should take to finish all courses.

CourseScheduleII.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.util.*;
public class CourseScheduleII {
public static int[] findOrder(int numCourses, int[][] prerequisites) {
// Build graph and in-degree
Map<Integer, List<Integer>> graph = new HashMap<>();
int[] inDegree = new int[numCourses];
for (int i = 0; i < numCourses; i++) {
graph.put(i, new ArrayList<>());
}
for (int[] prereq : prerequisites) {
int course = prereq[0];
int prerequisite = prereq[1];
graph.get(prerequisite).add(course);
inDegree[course]++;
}
// Queue for sources (in-degree 0)
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}
int[] order = new int[numCourses];
int index = 0;
while (!queue.isEmpty()) {
int course = queue.poll();
order[index++] = course;
for (int dependent : graph.get(course)) {
inDegree[dependent]--;
if (inDegree[dependent] == 0) {
queue.offer(dependent);
}
}
}
// If couldn't complete all courses, return empty
return index == numCourses ? order : new int[0];
}
public static void main(String[] args) {
int numCourses = 4;
int[][] prerequisites = {{1, 0}, {2, 0}, {3, 1}, {3, 2}};
int[] order = findOrder(numCourses, prerequisites);
System.out.print("Course order: ");
for (int course : order) {
System.out.print(course + " ");
}
System.out.println();
// Output: 0 1 2 3 (or 0 2 1 3 - both valid)
}
}

📝 Example 3: Alien Dictionary

Find the order of characters in an alien language given a sorted dictionary.

AlienDictionary.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import java.util.*;
public class AlienDictionary {
public static String alienOrder(String[] words) {
// Build graph
Map<Character, Set<Character>> graph = new HashMap<>();
Map<Character, Integer> inDegree = new HashMap<>();
// Initialize graph
for (String word : words) {
for (char c : word.toCharArray()) {
graph.putIfAbsent(c, new HashSet<>());
inDegree.putIfAbsent(c, 0);
}
}
// Build edges by comparing adjacent words
for (int i = 0; i < words.length - 1; i++) {
String word1 = words[i];
String word2 = words[i + 1];
int minLen = Math.min(word1.length(), word2.length());
// Find first different character
for (int j = 0; j < minLen; j++) {
char c1 = word1.charAt(j);
char c2 = word2.charAt(j);
if (c1 != c2) {
// c1 comes before c2
if (!graph.get(c1).contains(c2)) {
graph.get(c1).add(c2);
inDegree.put(c2, inDegree.get(c2) + 1);
}
break;
}
}
// Invalid case: word1 is prefix but comes after
if (word1.length() > word2.length() &&
word1.startsWith(word2)) {
return "";
}
}
// Topological sort
Queue<Character> queue = new LinkedList<>();
for (char c : inDegree.keySet()) {
if (inDegree.get(c) == 0) {
queue.offer(c);
}
}
StringBuilder result = new StringBuilder();
while (!queue.isEmpty()) {
char c = queue.poll();
result.append(c);
for (char next : graph.get(c)) {
inDegree.put(next, inDegree.get(next) - 1);
if (inDegree.get(next) == 0) {
queue.offer(next);
}
}
}
return result.length() == inDegree.size() ? result.toString() : "";
}
public static void main(String[] args) {
String[] words = {"wrt", "wrf", "er", "ett", "rftt"};
System.out.println("Alien order: " + alienOrder(words));
// Output: "wertf"
// From comparisons: w<e, t<f, r<t
}
}

🔑 Key Points to Remember

  • 1️⃣Uses in-degree (Kahn's algorithm) or DFS
  • 2️⃣If can't process all nodes → cycle exists
  • 3️⃣Time Complexity: O(V + E) where V=vertices, E=edges
  • 4️⃣Only works on Directed Acyclic Graphs (DAG)

💪 Practice Problems

  • Tasks Scheduling
  • All Tasks Scheduling Orders
  • Minimum Height Trees
  • Sequence Reconstruction