LeetCode/solutions/207. Course Schedule.md
2019-09-04 16:26:46 +08:00

96 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [207. Course Schedule](https://leetcode.com/problems/course-schedule/)
# 思路
仔细分析题目可知这道题的本质就是在有向图中检测环。而在有向图中检测环又可以看作是拓扑排序(不懂拓扑排序的童鞋可[参考此处](https://www.jianshu.com/p/b59db381561a))的应用.
拓扑排序又有两种基本思路: BFS和DFS.
## BFS
BFS对有向无环图(DAG, Directed Acyclic Graph)进行拓扑排序的基本思路是:
1. 从DAG中选择一个 没有前驱即入度为0的顶点并输出;
2. 从DAG中删除该顶点和所有以它为起点的有向边;
3. 重复 1 和 2 直到当前的 DAG 图为空。
稍加改动即可判断有向图中是否存在环:
1. 先将所有入度(in degree)为0的顶点入栈(或队列);
2. 从栈顶取出一个元素并用count自加计数, 将所有以其为起点的边的终点的入度减1, 此时若该终点入度为0则入栈;
3. 栈非空时, 重复2;
4. 最后判断count是否等于图的顶点数, 若不相等则说明有环.
为了方便找到以某个顶点为起点的所有边, 我们首先需要建一个二维数组G记录所有边, G[i]记录着以顶点i为起点的所有边的终点. DFS同.
**时间复杂度分析:**
设图有n个顶点e条边在拓扑排序的过程中搜索入度为零的顶点所需的时间是O(n)。
在正常情况下每个顶点进一次栈出一次栈所需时间O(n)。每个顶点入度减1的运算共执行了e次(若图基于邻接表)。所以总的时间复杂为O(n+e)。
## DFS
和常见的DFS一样, 我们需要一个一维数组 visited 来记录访问状态,但这里有三种状态:
* 0 表示还未访问过, 需要进一步调用递归函数;
* 1 表示已经访问了, 直接返回true即可;
* -1 表示正在访问, 表示遇到了两次, 有环, 返回false。
时间复杂度同BFS.
# C++
## BFS
``` C++
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
int arc_num = prerequisites.size();
stack<int>stk;
vector<int>in_degree(numCourses, 0);
vector<vector<int>>G(numCourses, vector<int>{});
for(auto &arc : prerequisites){ // 建图
in_degree[arc[0]]++;
G[arc[1]].push_back(arc[0]);
}
for(int i = 0; i < numCourses; i++) // 先将所有入度为0的顶点入栈
if(!in_degree[i]) stk.push(i);
int count = 0;
while(!stk.empty()){
int course = stk.top(); stk.pop();
count++;
for(int c: G[course]){ // 所有以course为起点的边的终点
if(!(--in_degree[c])) stk.push(c);
}
}
return count == numCourses;
}
};
```
## DFS
``` C++
class Solution {
private:
bool DFS(vector<vector<int>>&G, vector<int>& visited, int i) {
if (visited[i] == -1) return false;
if (visited[i] == 1) return true;
visited[i] = -1;
for (auto a : G[i]) {
if (!DFS(G, visited, a)) return false;
}
visited[i] = 1;
return true;
}
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> G(numCourses, vector<int>());
vector<int> visited(numCourses, 0);
for (auto &arc : prerequisites) {
G[arc[1]].push_back(arc[0]);
}
for (int i = 0; i < numCourses; ++i)
if (!DFS(G, visited, i)) return false;
return true;
}
};
```