读题:已知边,判断该有向图是否有环。
参考链接:「图解」拓扑排序 | 课程表问题
把一个 有向无环图 转成 线性的排序 就叫 拓扑排序
- BDF 广度遍历
- 1、根据pre得indegree入度数组、adject邻接表
- 2、queue保存入度为0的结点(即没有前提课程的课程)
- 3、根据queue减少相邻结点的入度(根据adject邻接表),减到0后入队列;
- 4、队列空了后,还有入度不为0的课程(即numCourse!=0)就返回False。
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
# 看有没有环, True:没有环
# 仔细读题:prerequisites相当于给出了边
# 广度遍历
inDegree = [0] * numCourses # 记录每个结点的入度
# print(inDegree)
adject = [[] for _ in range(numCourses)] # 通过边,填写二维数组邻接表
# adject = [[] * numCourses] # 这样不行! adject = [[]]
# print(adject)
for edge in prerequisites:
inDegree[edge[0]] += 1 # 入度加一
adject[edge[1]].append(edge[0]) # 选学了b才能学a,故边由b指向a
queue = [] # 队列中放入度为0的结点
for i in range(numCourses):
if inDegree[i] == 0: # 如果结点的入度为0,则入队列
queue.insert(0, i)
while queue:
prerequisite = queue.pop() # 取出入度为0的课程,调整学习该课程后的其他节点的入度(根据邻接表找)
numCourses -= 1 # 剩余学习课程-1
# print(adject)
# print(prerequisite)
for course in adject[prerequisite]:
# print(course)
inDegree[course] -= 1 # 入度-1,就是前提课程-1
if not inDegree[course]:
queue.insert(0, course) # 入度为0的结点入队列
return not numCourses # 如果剩余学习课程为0,就返回true
- 深度遍历
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
# 看有没有环, True:没有环
# 仔细读题:prerequisites相当于给出了边
# 深度遍历
self.adject = [[] for _ in range(numCourses)] # 邻接表
self.flag = [0 for _ in range(numCourses)]
# 标记,-1表示该结点已经被检查过不是构成环的结点;1表示本轮正在检查的结点,如果这轮又遇到了1就说明有环;0表示没有访问过
for edge in prerequisites:
self.adject[edge[1]].append(edge[0]) # 邻接表,学完前提课程edge[1],才能学edge[0]
def dfs(i):
if self.flag[i] == -1:
return True
if self.flag[i] == 1:
return False
self.flag[i] = 1 # 本轮遍历的标记
for j in self.adject[i]:
if dfs(j) == False:
return False
self.flag[i] = -1 # 经过检验了
return True
for i in range(numCourses):
if dfs(i) == False: # 遍历结点
return False
return True