题目

题目意思是某个课需要另外一个课作为先修课,需要判断所有课程能不能完成,不能完成的情况是这些课程形成了环。
例如:

1
2
Input: 3, [[1,2],[0,1],[2,0]]
2->1->0->2 形成了环,这样就不能完成课程,返回false

方法一 dfs

可以直接暴力对所有节点DFS,然后在每个节点DFS时判断之前是否以及访问当前节点,如果已经访问则返回false,否则继续向下DFS。这样时间复杂度是O(n^2),因此使用finished记录已经访问过的节点是否能完成的信息,再之后再次访问时不需要继续DFS,使得时间复杂度下降。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<vector<int> > edges(numCourses, vector<int>(numCourses, -1));
for (pair<int, int> pair : prerequisites) {
edges[pair.second][pair.first] = 1;
}

for (int i = 0; i < numCourses; i++) {
if (!dfs(i, edges)) return false;
}
return true;
}
bool dfs(int start, vector<vector<int> > & edges) {
if (finished.find(start) != finished.end()) return true;
for (int i = 0; i < edges[start].size(); i++) {
if (edges[start][i] == -1) continue;
if (visited.find(i) != visited.end()) return false;
visited.insert(i);
if(!dfs(i, edges)) return false;
finished.insert(i);
visited.erase(i);
}
return true;
}
unordered_set<int> visited;
unordered_set<int> finished;
};

时间复杂度O(n)

方法二 BFS

因为拓扑排序是不能包括环的,所以直接判断能否拓扑排序,就能知道是否能完成。
使用BFS来实现拓扑排序,需要队列以及获得节点的入度,首先入度为0的节点被访问,然后将相关联的节点度减一,直到入度为0,再次入队,最后判断能否将所有节点访问,如果不能就false。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<int> indegree(numCourses, 0);
vector<vector<int> > edges(numCourses, vector<int>(numCourses, -1));
for (pair<int, int> pair : prerequisites) {
edges[pair.second][pair.first] = 1;
indegree[pair.first]++;
}
queue<int> starts;

for (int i = 0; i < indegree.size(); i++) {
if (indegree[i] == 0) {
starts.push(i);
}
}
while (!starts.empty()) {
numCourses--;
int start = starts.front();
starts.pop();
for (int end = 0; end < edges[start].size(); end++) {
if (edges[start][end] == -1) continue;
indegree[end]--;
if (indegree[end] == 0) {
starts.push(end);
}
}
}
return numCourses == 0;
}
};

时间复杂度O(n)