我们需要判断是否能完成所有指定课程的学习,其中numCourses表示课程总数,prerequisites表示先修关系(例如[0,1]表示学习课程0前需先完成课程1)。
能完成课程学习的情况: 当课程的先修关系可以形成有效的拓扑排序时,例如[0,1],[1,2],[2,3],学习顺序必须为3→2→1→0,这样就能顺利完成所有课程。
无法完成课程学习的情况: 当课程间存在循环依赖关系时,例如[0,1],[1,2],[2,0],会导致无法确定学习顺序(学习0需要1,1需要2,而2又需要0),形成死锁状态。
我们可以用有向图来直观表示这两种情况。
通过分析可以发现,当有向图中存在环时,说明课程之间存在循环依赖关系,此时无法完成所有课程的学习。因此,我们需要判断有向图中是否存在环:若有环则返回false,无环则返回true。
算法步骤如下:
-
构建图结构:将prerequisites数组中的每个元素p[1]添加到p[0]的邻接表中
-
搜索过程:对所有未被标记的节点进行深度优先搜索
-
DFS实现逻辑:
- 检查当前路径是否存在环(即当前路径是否已包含正在搜索的节点)
- 标记当前节点并将其加入当前路径
- 递归搜索该节点的所有邻接节点,若发现环则立即返回true
- 完成搜索后进行回溯,将该节点从当前路径中移除
class Solution {
public:bool dfs(int i,vector<int>& vis,vector<int>& recStack,vector<vector<int>>& g) {if (recStack[i]) return true;if (vis[i]) return false;recStack[i] = 1;vis[i] = 1;for (auto it:g[i]) {if (dfs(it,vis,recStack,g)) {return true;}}recStack[i] = 0;return false;}bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {vector<vector<int>> g(numCourses);for (auto &p:prerequisites) {g[p[1]].push_back(p[0]);}vector<int> vis(numCourses,0);vector<int> recStack(numCourses,0);for (int i = 0;i < numCourses;i++) {if (!vis[i]) {if (dfs(i,vis,recStack,g)){return false;}}}return true;}
};
时间复杂度:O(n + m),其中n为课程数numCourses,m为邻接表长度,每个节点仅遍历一次
空间复杂度:O(n + m),邻接表g需要存储n+m个元素