题目链接:点击这里
题意: 你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse - 1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
方法一:基于BFS
- 定义一个队列 Q Q Q,并把所有入度为 0 0 0 的结点加入队列。
- 取队首结点输出。然后删去所有从它出发的边,并令这些边到达的顶点的入度减 1 1 1,如果某个顶点的入度减为 0 0 0,则将其加入队列。
- 反复进行②操作,直到队列为空。
判定:如果队列为空时入过队的结点数目恰好为N,说明拓扑排序成功,图G为有向无环图;否则,拓扑排序失败,图G中有环。
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> graph(numCourses); // 邻接表
vector<int> inDegree(numCourses); // 入度
for(auto it : prerequisites)
{
graph[it[1]].emplace_back(it[0]);
inDegree[it[0]]++;
}
int num = 0; // 记录加入拓扑序列的顶点数
queue<int> q;
for(int i = 0; i < inDegree.size(); i++) // 将所有入度为0的顶点入队
if(inDegree[i] == 0)
q.push(i);
while(!q.empty())
{
int u = q.front(); // 取队首顶点u
q.pop();
for(int i = 0; i < graph[u].size(); i++)
{
int v = graph[u][i]; // u的后继顶点v
if(--inDegree[v] == 0) // 顶点v的入度减1
q.push(v); // 顶点v的入度减为0则入队
}
num++;
}
if(num == numCourses) return true;
else return false;
}
};
方法二:基于DFS
用 f l a g [ ] flag[\ ] flag[ ] 标记每一个节点的状态, 0 0 0 表示没有访问过, 1 1 1 表示当前的dfs正在访问, − 1 -1 −1 表示已经访问完毕。
对所有的节点进行遍历。比如遍历节点 a a a 的时候,遍历前,先将该节点 a a a 的 f l a g flag flag 设置为 1 1 1,表示正在遍历,然后,对 a a a 的邻接表进行遍历:
- 如果下一级节点 b b b 的 f l a g flag flag 为 − 1 -1 −1,说明已经遍历了,返回 t u r e ture ture;
- 如果为 1 1 1,说明重复遍历了当前的节点,这个时候存在环,返回 f a l s e false false;
- 如果为 0 0 0,则继续遍历该节点b的邻接表节点。
class Solution {
public:
bool dfs(int v, vector<vector<int>>& graph, vector<int>& flag)
{
if(flag[v] == 1) return false;
if(flag[v] == -1) return true;
flag[v] = 1;
for(auto it : graph[v])
if(!dfs(it, graph, flag))
return false;
flag[v] = -1;
return true;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> flag(numCourses, 0); // 初始化为没有访问过
vector<vector<int>> graph(numCourses); // 邻接表
for(auto it : prerequisites)
graph[it[1]].emplace_back(it[0]);
for(int i = 0; i < graph.size(); i++)
if(!dfs(i, graph, flag))
return false;
return true;
}
};