现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
说明:
输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
提示:
这个问题相当于查找一个循环是否存在于有向图中。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。
通过 DFS 进行拓扑排序 - 一个关于Coursera的精彩视频教程(21分钟),介绍拓扑排序的基本概念。
拓扑排序也可以通过 BFS 完成。
思路
下图为课程的依赖关系
- 图1为可完成全部任务
- 图2存在环:如果要修2课程,需要先修1课程,而修1课程的前提是修完2、3课程,存在冲突
所以判断图是否存在环即可。
通过图的深搜来完成 - 1、首先连接好图的结构,初始化label为-1,表示未访问过。0为正在访问,1为已访问完成
- 2、dfs:
- 开始第i节点,设visit[i]为0
- 然后遍历它的相邻节点
- if:相邻节点为-1,即未访问过,则dfs(相邻节点)
- else if:相邻节点为0,即相邻节点为正在访问的节点,此时形成环,return false
- 访问完全部相邻节点,且相邻节点也没有形成环,则都标记为1。
- 3、完成return true。
struct GraphNode{
int label;
std::vector<GraphNode*> neibhors;
GraphNode(int x): label(x) {};
};
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
std::vector<GraphNode*> graph;//图的邻接表
std::vector<int> visit;//节点访问状态数组,-1 未防蚊,0 正在访问,1 访问过
for(int i = 0; i < numCourses; i++){
graph.push_back(new GraphNode(i));
visit.push_back(-1);
}//创建图的节点,new的一般要delet,visit初始化
for(int i = 0; i < prerequisites.size(); i++){
GraphNode *begin = graph[prerequisites[i][1]];
GraphNode *end = graph[prerequisites[i][0]];
begin->neibhors.push_back(end);
}//由题,课程2指向课程1,按照图的实际意义,先学begin,在学end,箭头代表学习路径
for (int i = 0; i < graph.size(); i++){//循环所有初始节点
if (visit[i] == -1 && !DFS_graph(graph[i], visit)){
return false;//如果节点没有被访问过,进行DFS,如果遇到环,返回false
}
}
for (int i = 0; i < numCourses; i++){
delete graph[i];
}
return true;
}
private://visit -1表示还没被访问,0表示正在递归访问,1表示访问过
bool DFS_graph(GraphNode *node, std::vector<int> &visit){
visit[node->label] = 0;//标记此时正在访问node->label这个初始节点
for(int i = 0; i < node->neibhors.size(); i++){//递归搜索初始节点全部邻接点
if(visit[node->neibhors[i]->label] == -1){//如果邻接点没被访问过,下面开始深度递归访问
if(DFS_graph(node->neibhors[i], visit) == 0){
return false;//如果递归没退出,遇到正在访问过程中的任何一个节点,返回0
}
}
else if(visit[node->neibhors[i]->label] == 0){
return false;//这就是两个课程,有环,直接就是自己遇到了自己,返回0
}
}
visit[node->label] = 1;//访问过后,node->label标记为访问过了
return true;
}
};