一、题目分析
(1)题目问题可以抽象为检测一个有向图是否存在环。若有,则 return False
;没有,则 return True
。
(2)对于一个有向图,我们可以一个一个地删除入度为零的节点,每删除一个,与其相连的有向线段当然也要跟着删除(即以该点为前序课程的点入度-1)。容易分析得到,这个过程不会破坏图中的环(如果存在的话。环中每一个点的入度都不为零,所以不可能被删除。)
因此,迭代这个过程,直到不存在入度为零的点。此时若图仍然存在(仍然存在点),则可判断有环的存在。
(3)现上述原理:
关于有向图的表示:鉴于题目所给的输入都是int
型数据,所以可以直接用两个数组,一个一维数组,用来存放每个编号课程所代表的课程的 入度,其索引即为课程编号;另一个是二维数组,用来存放以该索引数字所代表的课程的后续课程。
由任意一个入度为0的点开始,遍历整个图,依次删除入度为0的点。遍历采用BFS算法或者DFS算法都可以。
二、题目解法及代码
2.1
这里采用BFS算法,参考这个高赞的答案,写的很棒,初始化变量就把我这个小白惊呆了。第一遍看从头懵到尾。第二天去看了一天的BFS和DFS算法(关于这个墙裂推荐B站UP主‘正月点灯笼’的教学视频,链接奉上。),从原理到实现,第三天才看明白,并且能够做一些小的改动了。比如答案中遍历用的是collections库中的deque双向队列。我也不是太熟悉,搜了搜,感觉还是用前一天先学的普通的queue就可以实现本问题。于是自己复现的时候直接用的是普通的数组了。
详细解释代码如下:
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
#### Initialize the required variables
indegress = [0 for _ in range(numCourses)] # 一维数组,存放入度数
adjacency = [[]for _ in range(numCourses)] # 二维数组,存放后续课程
queue = [] # 用来实现遍历的队列
nums = numCourses # 记录图中剩余的节点
### Represent the graph
for cour,pre in prerequisites:
indegress[cour] += 1
adjacency[pre].append(cour)
### Put the first batch of vertexes whoesindegrees is 0 into the queue
for i in range(len(indegress)):
if indegress[i] == 0:
queue.append(i)
### Traverse the entire graph, removing points with 0 indegree
while queue:
ver = queue.pop(0) # 这里无所谓是pop(0)或者pop(),前者为BFS,后者为DFS算法。
nums -= 1 # 遍历一个节点,节点数减一
for i in adjacency[ver]:
indegress[i] -= 1 # 对于遍历到并删除的入度为0的点,同时删除
if not indegress[i] : queue.append(i)
### Returns True if the number of remaining vertexes is 0
return not nums
另外对于输入的课程信息不是用int
型的数据表示的,而是用str
表示的话,indegress
和adjacency
可以选择字典型数据来构造,同样分别是一维的和二维的。