课程安排
题目
你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-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。这是不可能的。
提示:
1.输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
2.你可以假定输入的先决条件中没有重复的边。
3.1 <= numCourses <= 105
思路
对于n个课程,它们之间有m个依赖关系,可以看成顶点个数为n,边个数为m的有向图。
如:图1:n = 3,m = [[0,1],[0,2][1,2]];可以完成。
图2:n = 3, m = [[0,1],[1,2],[2,0]];不可以完成。
利用节点访问状态,-1没有访问过,0代表正在访问,1表示已完成访问。
有关图的基础知识点击:图的基础知识
方法1:使用深度优先搜索,如果正在探索的某一顶点(还未推出递归深度搜索),又回到了已经搜索过的顶点,即证明图有环。(如下图所示)
代码实现如下:
struct GraphNode{
int label;
vector<GraphNode*> neighbors;
GraphNode(int x) : label(x) {};
};
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<GraphNode*> graph;
vector<int> visit;
for(int i = 0; i < numCourses; i++){
graph.push_back(new GraphNode(i)); //创建图的节点,并赋访问状态为空
visit.push_back(-1); //创建图,连接图的顶点
}
for(int i = 0; i < prerequisites.size(); i++){
GraphNode* begin = graph[prerequisites[i][1]];
GraphNode* end = graph[prerequisites[i][0]];
begin->neighbors.push_back(end);
}
for(int i = 0; i < graph.size(); i++){
if(visit[i] == -1 && !DFS_graph(graph[i],visit)){
return false;
}
}
for(int i = 0; i < numCourses; i++){
delete graph[i];
}
return true;
}
private:
bool DFS_graph(GraphNode* node, vector<int> &visit){
visit[node->label] = 0;
for(int i = 0; i < node->neighbors.size(); i++){
if(visit[node->neighbors[i]->label] == -1 ){ //当该顶点未被访问
if(DFS_graph(node->neighbors[i], visit) == 0){ //再次进行深度访问
return false;
}
}
else if (visit[node->neighbors[i]->label] == 0){
return false;
}
}
visit[node->label] = 1;
return true;
}
};
方法2:利用宽度优先搜索,只将入度为0的点添加至队列。当完成一个顶点的搜索(从队列取出),它指向的所有顶点入读都减1,若此时某顶点入度为0则添加至队列,若完成宽度搜索后,所有的顶点入度都为0,则图无环,否则有环。
过程如下图所示:
无环时
有环时
实现代码如下:
struct GraphNode{
int label;
vector<GraphNode*> neighbors;
GraphNode(int x) : label(x) {};
};
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<GraphNode*> graph;
vector<int> degree;
for(int i = 0; i < numCourses; i++){
degree.push_back(0);
graph.push_back(new GraphNode(i));
}
for(int i = 0; i < prerequisites.size(); i++){
GraphNode* begin = graph[prerequisites[i][1]];
GraphNode* end = graph[prerequisites[i][0]];
begin->neighbors.push_back(end);
degree[prerequisites[i][0]]++;//入度+1
}
queue<GraphNode*> Q;
for(int i = 0; i < numCourses; i++){
if(degree[i] == 0){
Q.push(graph[i]);
}
}
while(!Q.empty()){
GraphNode* node = Q.front();
Q.pop();
for(int i = 0; i < node->neighbors.size(); i++){
degree[node->neighbors[i]->label]--;
if(degree[node->neighbors[i]->label] == 0){
Q.push(node->neighbors[i]);
}
}
}
for(int i = 0; i < graph.size(); i++){
delete graph[i];
}
for(int i = 0; i < degree.size(); i++){
if(degree[i]){
return false;
}
}
return true;
}
};
致谢
本章知识点和思路由小象学院相关视频提供,由本人学习并梳理得出,希望自己加深记忆的同时,也能给大家提供更多有关于一些算法的知识点。
你的点赞、评论、收藏就是对我最大的支持与鼓励,谢谢!