By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
"This pattern appears in 1 out of every 3 onsite interviews—nail it, and you prove you can model real-world dependencies, detect cycles, and optimize schedules—exactly what FAANG expects from senior engineers."
Before diving in, ensure you understand: 1. Graph Representations (Adjacency List vs. Matrix) 2. DFS/BFS Traversal (Visited states, recursion stack) 3. Indegree Concept (Number of incoming edges to a node)
A → B
A
B
A → B → C → A
indegree[B] = 1
visited[node] = 1
2
indegree = 0
Repeat this checklist for every "Course Schedule" problem:
Represent prerequisites as directed edges (A → B means A is a prerequisite for B).
Choose the Right Approach
DFS + Cycle Detection: Best for checking feasibility (cycle = no valid order).
Initialize Data Structures
graph = defaultdict(list)
indegree = [0] numCourses
Visited Array (DFS): visited = [0] numCourses (0 = unvisited, 1 = processing, 2 = processed)
visited = [0] numCourses
Build the Graph & Indegree
For each prerequisite [A, B], add B → A to the graph and increment indegree[A].
[A, B]
B → A
indegree[A]
Execute the Algorithm
indegree
DFS:
visited[node] == 1
Check for Cycles (DFS) or Valid Order (Kahn’s)
False
Kahn’s: If result length ≠ numCourses, cycle exists.
numCourses
Return the Result
True/False
Problem: Can you finish all courses given prerequisites? Return True if possible, else False.
True
Framework Application: 1. Model as Graph: - numCourses = 2, prerequisites = [[1,0]] → 0 → 1. 2. Choose Approach: DFS (cycle detection). 3. Initialize: - graph = {0: [1], 1: []} - visited = [0, 0] 4. Build Graph: Already done. 5. DFS: - Start at 0: - visited[0] = 1 (processing). - Visit 1: - visited[1] = 1. - No neighbors → backtrack, mark 1 as processed (visited[1] = 2). - Backtrack, mark 0 as processed (visited[0] = 2). 6. Check Cycle: No cycle → return True.
numCourses = 2
prerequisites = [[1,0]]
0 → 1
graph = {0: [1], 1: []}
visited = [0, 0]
0
visited[0] = 1
1
visited[1] = 1
visited[1] = 2
visited[0] = 2
Code (DFS):
def canFinish(numCourses, prerequisites): graph = defaultdict(list) for a, b in prerequisites: graph[b].append(a) visited = [0] numCourses # 0=unvisited, 1=processing, 2=processed def has_cycle(node): if visited[node] == 1: return True if visited[node] == 2: return False visited[node] = 1 for neighbor in graph[node]: if has_cycle(neighbor): return True visited[node] = 2 return False for node in range(numCourses): if has_cycle(node): return False return True
Time Complexity: O(V + E) (nodes + edges). Space Complexity: O(V + E) (graph + recursion stack).
O(V + E)
Why This Works: - DFS detects cycles by tracking nodes in the current recursion stack (visited[node] == 1). - If a node is revisited while still processing, a cycle exists.
Problem: Return an order of courses to take, or [] if impossible.
[]
Framework Application: 1. Model as Graph: Same as Example 1. 2. Choose Approach: Kahn’s (BFS + Indegree). 3. Initialize: - graph = {0: [1], 1: []} - indegree = [0, 1] - queue = deque([0]) 4. Build Graph: Already done. 5. Kahn’s: - Dequeue 0, add to result. - Decrement indegree[1] → indegree[1] = 0. - Enqueue 1, dequeue 1, add to result. 6. Check Validity: Result length = numCourses → return [0, 1].
indegree = [0, 1]
queue = deque([0])
indegree[1]
indegree[1] = 0
[0, 1]
Code (Kahn’s):
from collections import deque def findOrder(numCourses, prerequisites): graph = defaultdict(list) indegree = [0] numCourses for a, b in prerequisites: graph[b].append(a) indegree[a] += 1 queue = deque([node for node in range(numCourses) if indegree[node] == 0]) result = [] while queue: node = queue.popleft() result.append(node) for neighbor in graph[node]: indegree[neighbor] -= 1 if indegree[neighbor] == 0: queue.append(neighbor) return result if len(result) == numCourses else []
Time Complexity: O(V + E). Space Complexity: O(V + E).
Why This Works: - Kahn’s processes nodes with no dependencies first, ensuring a valid order. - If the result length ≠ numCourses, a cycle exists.
Problem: Given courses with [duration, lastDay], return the max number of courses you can take without overlapping.
[duration, lastDay]
Framework Application: 1. Model as Graph: Not a direct topological sort, but uses greedy + priority queue. 2. Key Insight: - Sort courses by lastDay. - Use a max-heap to track durations. - If adding a course exceeds lastDay, remove the longest course in the heap. 3. Algorithm: - Sort courses by lastDay. - Iterate, adding durations to a max-heap. - If current_time + duration > lastDay, pop the longest course.
lastDay
current_time + duration > lastDay
Code:
import heapq def scheduleCourse(courses): courses.sort(key=lambda x: x[1]) # Sort by lastDay max_heap = [] current_time = 0 for duration, lastDay in courses: if current_time + duration <= lastDay: heapq.heappush(max_heap, -duration) current_time += duration elif max_heap and -max_heap[0] > duration: longest = -heapq.heappop(max_heap) current_time -= longest heapq.heappush(max_heap, -duration) current_time += duration return len(max_heap)
Time Complexity: O(N log N) (sorting + heap operations). Space Complexity: O(N) (heap).
O(N log N)
O(N)
Why This Works: - Greedy choice: Always prioritize courses with earlier deadlines. - Max-heap ensures we replace the longest course if needed.
prerequisites = []
[0, 1, ..., numCourses-1]
"Alright, let’s lock this in. For ‘Course Schedule’ problems, always:
You’ve got this. Walk in, sketch the graph, and crush it. Good luck!
Final Note: Practice with variations (e.g., weighted courses, parallel processing). The framework is your safety net—use it every time.
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.