链接:https://leetcode-cn.com/problems/course-schedule/
题解:https://leetcode-cn.com/problems/course-schedule/solution/tuo-bu-pai-xu-by-liweiwei1419/
https://leetcode-cn.com/problems/course-schedule/solution/ctuo-bu-pai-xu-dfs-by-zb121/
复杂度分析:
时间复杂度:O(E + V)。这里 EE表示邻边的条数,V表示结点的个数。初始化入度为 0 的集合需要遍历整张图,具体做法是检查每个结点和每条边,因此复杂度为 O(E+V),然后对该集合进行操作,又需要遍历整张图中的每个结点和每条边,复杂度也为 O(E+V);
空间复杂度:O(E+V):邻接表长度是 V,每个课程里又保存了它所有的边。
class Solution {
public:
// kahn算法
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
// 存储有边的两个节点,<key, value>, key表示开始节点,value表示截止节点,表示方向
std::unordered_map<int, set<int>> adj;
// 入度个数
vector<int> in_degree(numCourses, 0);
for(int i = 0; i < prerequisites.size(); ++i) {
int end = prerequisites[i][0];
int begin = prerequisites[i][1];
++in_degree[end];
// 表示begin与end相邻,并且方向是
adj[begin].insert(end);
}
std::queue<int> que;
// 获得入度为0的节点
for(int i = 0; i < numCourses; ++i) {
if(in_degree[i] == 0) {
que.push(i);
}
}
int count = 0;
// 遍历入度为0的节点
while(!que.empty()) {
int front = que.front();
que.pop();
// 已经走过的节点个数
++count;
for(auto ite : adj[front]) {
// 和front相邻的节点的入度都减少1
--in_degree[ite];
// 将入度为0的节点放入队列
if(in_degree[ite] == 0) {
que.push(ite);
}
}
}
return count == numCourses;
}
};
class Solution {
public:
bool dfs(int node, vector<bool>& visited, std::unordered_map<int, vector<int>>& edges) {
// 如果这个节点访问过,就存在环了
if(visited[node]) {
return true;
}
visited[node] = true;
for(auto& edge : edges[node]) {
if(dfs(edge, visited, edges)) {
return true;
}
}
visited[node] = false;
return false;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
if(prerequisites.size() <= 0) {
return true;
}
vector<bool> visited(numCourses, false);
// 邻接表
std::unordered_map<int, vector<int>> edges;
for(int i = 0; i < prerequisites.size(); ++i) {
edges[prerequisites[i][0]].push_back(prerequisites[i][1]);
}
for(int i = 0; i < numCourses; ++i) {
// 判断以每个节点为必学可能,是否存在环
if(dfs(i, visited, edges)) {
return false;
}
}
return true;
}
};