1. 题目
2. 解题思路一
首先,我们建立一个有向图,如果学习课程 i i i 必须先学习课程 j j j,那么我们就新增一条边由 i i i 指向 j j j。第一种思路是在图中进行深度优先搜索,一开始,每个节点的状态都设置为 0 0 0,代表没有访问。针对每一个没有被访问的节点,我们将其状态设置为 1 1 1,代表正在访问,然后遍历这个节点的每一条边:
- 如果下个节点的状态为 0 0 0,递归进行搜索;
- 如果下个节点的状态为 1 1 1,代表此节点第二次被访问,课程的依赖关系构成了一个环,直接返回 f a l s e false false;
- 如果下个节点的状态为 2 2 2,从这个节点往下的路径都已经访问完成,无需处理继续走;
如果当前节点的所有边都访问完了,则将其状态设置为 2 2 2,代表访问完成。
3. 代码实现一
class Solution {
public:
vector<vector<int> > edges;
vector<int> state;
bool valid;
void dfs(int i) {
if (!valid) {
return;
}
state[i] = 1;
for (auto j : edges[i]) {
if (state[j] == 0) {
dfs(j);
} else if (state[j] == 1) {
valid = false;
return;
}
}
state[i] = 2;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
valid = true;
edges.resize(numCourses);
state.resize(numCourses);
for (auto& info: prerequisites) {
edges[info[0]].push_back(info[1]);
state[info[0]] = 0;
}
for (size_t i = 0; i < numCourses && valid; ++i) {
if (state[i] == 0) {
dfs(i);
}
}
return valid;
}
};
4. 解题思路二
首先还是建立一个有向图,只不过这次,如果学习课程
i
i
i 必须先学习课程
j
j
j,那么我们就新增一条边由
j
j
j 指向
i
i
i,同时定义一个数组 preCourses
代表每一门课程需要先学习的课程总数。如果某一门课程
j
j
j 需要先学习的课程总数为
0
0
0,那么这门课程就可以完成学习,我们将其放入队列。
此时,如果有另一门课程
i
i
i 需要先学习课程
j
j
j,那么它需要先学习的课程总数就减少了一门。遍历课程
j
j
j 的所有边,对所有下一个节点的 preCourses
都减少
1
1
1。如果有新的课程满足 preCourses==0
,同样也将其加入队列。
最后,如果所有课程的 preCourses==0
,那么所有课程也就都可以完成学习了。
5. 代码实现二
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int> > edges(numCourses);
vector<int> preCourses(numCourses, 0);
for (auto& info: prerequisites) {
edges[info[1]].push_back(info[0]);
preCourses[info[0]]++;
}
queue<int> q;
int visit_num = 0;
for (size_t i = 0; i < numCourses; ++i) {
if (preCourses[i] == 0) {
q.push(i);
visit_num++;
}
}
while (!q.empty()) {
int i = q.front();
for (auto j : edges[i]) {
if (--preCourses[j] == 0) {
q.push(j);
visit_num++;
}
}
q.pop();
}
return visit_num == numCourses;
}
};