链接:https://leetcode-cn.com/problems/parallel-courses/solution/ping-xing-ke-cheng-by-leetcode-solution/
题目来源:力扣(LeetCode)
拓扑排序中最前面的节点,该节点一定不会有任何入边,也就是它没有任何的先修课程要求。当我们将一个节点加入答案中后,我们就可以移除它的所有出边,代表着它的相邻节点少了一门先修课程的要求。如果某个相邻节点变成了「没有任何入边的节点」,那么就代表着这门课可以开始学习了。按照这样的流程,我们不断地将没有入边的节点加入答案,直到答案中包含所有的节点(得到了一种拓扑排序)或者不存在没有入边的节点(图中包含环)。
算法
我们使用一个队列来进行广度优先搜索。开始时,所有入度为 0 的节点都被放入队列中,它们就是可以作为拓扑排序最前面的节点,并且它们之间的相对顺序是无关紧要的。
在广度优先搜索的每一步中,我们取出队首的节点 u:
我们将 u 放入答案中;
我们移除 u 的所有出边,也就是将 u 的所有相邻节点的入度减少 1。如果某个相邻节点 v 的入度变为 0,那么我们就将 v 放入队列中。
在广度优先搜索的过程结束后。如果答案中包含了这 n 个节点,那么我们就找到了一种拓扑排序,否则说明图中存在环,也就不存在拓扑排序了。
#存储每个节点的入度(0~n-1)
precourse_cnt = [0]*n
# 存储有向图
dic = collections.defaultdict(list)
for edge in relations:
dic[edge[0]].append(edge[1])
precourse_cnt[edge[1]-1]+=1
# 将所有入度为 0 的节点放入队列中(1~n)
queue=[course for course in range(1,n+1) if precourse_cnt[course-1]==0]
term = 0#判断有多少段并行课程的时间段才能上完所有的课
while queue:
next_semester = []#1~n
#搞定queue中所有入度为0的课程
for cur_course in queue:
for neigh in dic[cur_course]:
#neigh入度(0~n-1)减去1
precourse_cnt[neigh-1]-=1
if precourse_cnt[neigh-1]==0:
next_semester.append(neigh)
#queue中为所有新的入度为0的课程
queue = next_semester
term+=1
#check whether cycle exists
#存储每个节点的入度为0才有topologial order,否则存在cycle
if sum(precourse_cnt)!=0:
return -1
return term