原题
There are a total of n courses you have to take, labeled from 0 to n - 1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
For example:
2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.
2, [[1,0],[0,1]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.
Note:
1,The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
2,You may assume that there are no duplicate edges in the input prerequisites.
leetCode地址:https://leetcode.com/problems/course-schedule/description/
解题思路
题目大意:有n门课程,其中有些课程是有先决条件的,例如[1, 2]表示必须先上完课程2,才能上课程1。给出课程数与所有先决条件,求是否能学完所有课程。
我们可以将一门课程作为图的一个节点,一个先决条件就是一条有向边,就可以将此问题转化为关于图问题了。我们要知道能不能上完所有课程,其实要求的就是,图里有没有环路。如果图里有环,就不能上完所有课程。
现在问题变成了—验证有向图是否有环。
验证环路的解法有很多种,例如可以使用拓扑排序来验证:如果对所有节点的拓扑排序能够完成,那么图里就没有环,否则就有环。拓扑排序的方法也有两种,这里就不再一一展开。
我们也可以利用DFS遍历来进行验证,不过这里的DFS需要一点处理。
每个节点在遍历过程中有三种状态:0表示未访问,1表示正在访问其子节点,2表示此节点以及其所有子节点均已访问。
这三种状态有什么用处呢?当我们遍历到某个节点,如果此节点的状态为0或者2,都没有问题;但如果状态为1,就表明此处形成了环。为什么呢?因为状态为1时,表示正在遍历其子节点;而子节点又能够访问到它,必须要有回路才能做到。
通过以上思路就可以设计出DFS求解的算法。
代码
class Solution {
public:
int visited[10000]; //节点的状态保存
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
map<int, vector<int> > graph;
//初始化过程
for (int i = 0; i < numCourses; i++ ) {
vector<int> temp;
graph[i] = temp;
}
//所有节点的状态初始化为状态0
for (int& i : visited) i = 0;
//构建有向图
for (auto i : prerequisites) {
graph[i.first].push_back(i.second);
}
//对每个节点都进行dfs检验
for (int i = 0; i < numCourses; i++) {
if (visited[i] == 0) {
if (!dfs(graph, i)) return false;
}
}
return true;
}
bool dfs(map<int, vector<int> >& graph, int t) {
if (visited[t] == 1) return false;
//遍历子节点之前,将状态设为1
visited[t] = 1;
// 开始遍历子节点
for (auto i : graph[t]) {
if (dfs(graph, i) == false) return false;
}
// 遍历完成后,状态设为2
visited[t] = 2;
return true;
}
};
总结
1、有向图的环路检验
2、dfs算法
3、拓扑排序