There are a total of numCourses
courses you have to take, labeled from 0
to numCourses - 1
. You are given an array prerequisites
where prerequisites[i] = [ai, bi]
indicates that you must take course bi
first if you want to take course ai
.
[0, 1]
, indicates that to take course 0
you have to first take course 1
.Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.
Example 1:
Input: numCourses = 2, prerequisites = [[1,0]] Output: [0,1] Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1].
Example 2:
Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]] Output: [0,2,1,3] Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3].
Example 3:
Input: numCourses = 1, prerequisites = [] Output: [0]
Constraints:
1 <= numCourses <= 2000
0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= ai, bi < numCourses
ai != bi
[ai, bi]
are distinct.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Imagine you have a list of courses, and some courses must be taken before others. The brute force approach tries out every possible order you could take the courses in, checking if that order respects all the prerequisites.
Here's how the algorithm would work step-by-step:
def find_order_brute_force(number_of_courses, prerequisites):
import itertools
all_courses = list(range(number_of_courses))
all_possible_orders = list(itertools.permutations(all_courses))
valid_schedules = []
for possible_order in all_possible_orders:
is_valid = True
# Check if the current order satisfies all prerequisites.
for course, prerequisite_list in prerequisites.items():
for prerequisite in prerequisite_list:
if possible_order.index(course) < possible_order.index(prerequisite):
is_valid = False
break
if not is_valid:
break
# If the order is valid, add it to the list of valid schedules.
if is_valid:
valid_schedules.append(list(possible_order))
# Return a valid schedule if one exists, otherwise return an empty list.
if valid_schedules:
return valid_schedules[0]
else:
return []
The problem involves figuring out the correct order to take courses, considering some courses have prerequisites. We use a method that systematically works backward from courses with no prerequisites to build the course schedule. This ensures we only add courses to the schedule after all their prerequisite courses have been added.
Here's how the algorithm would work step-by-step:
def find_order(number_of_courses, prerequisites):
in_degree = [0] * number_of_courses
adjacency_list = [[] for _ in range(number_of_courses)]
for course, prerequisite in prerequisites:
in_degree[course] += 1
adjacency_list[prerequisite].append(course)
course_order = []
queue = []
# Start with courses having no prerequisites
for course in range(number_of_courses):
if in_degree[course] == 0:
queue.append(course)
while queue:
course = queue.pop(0)
course_order.append(course)
# Reduce in-degree of dependent courses
for dependent_course in adjacency_list[course]:
in_degree[dependent_course] -= 1
# Add course to queue if no more prerequisites
if in_degree[dependent_course] == 0:
queue.append(dependent_course)
# Detect cycle: Not all courses can be taken
if len(course_order) != number_of_courses:
return []
return course_order
Case | How to Handle |
---|---|
numCourses is 0 or negative | Return an empty list, as no courses need to be taken. |
prerequisites is null or empty | Return a list of courses from 0 to numCourses-1 in any order, as no dependencies exist. |
prerequisites contains a self-loop (e.g., [0, 0]) | The algorithm should detect this cycle and return an empty list, indicating no valid schedule. |
prerequisites contains duplicate entries (e.g., [1, 0], [1, 0]) | The graph construction logic should handle duplicates by treating them as a single dependency edge. |
A cycle exists in the prerequisites, making it impossible to finish all courses | The topological sort algorithm must detect cycles and return an empty list. |
numCourses is 1 and prerequisites is empty | Return a list containing only course 0, as only one course exists and has no dependencies. |
Large number of courses and prerequisites stressing memory or causing stack overflow with recursion | The solution should use an iterative topological sort approach (e.g., Kahn's algorithm) to avoid stack overflow, and the data structures used should scale efficiently in memory (e.g., adjacency lists). |
Multiple valid course orderings exist | The algorithm should return one of the valid orderings, as the problem statement does not require a specific ordering. |