207. Course Schedule

仅供自己学习

 

思路:

题目给出的 prerequisites 实际可以转化为一个有向图,那么既然有明确的先后顺序排列才能完成课程学习,这就要求这组数据组成的图不能是有向有环图,所以应该往DFS和BFS想,只要搜索整张图没有发现环,那么就可以输出true,否则输出false。按照题目所说,prerequisites[1]是指向prerequisites[0]的。上述也就是一种拓扑排序,即当有向图中的任意一条边(u,v),u都在v前的排列,则该排列就是有向图的拓扑排序。

 DFS:此时结点可能会有三个状态,未探索,探索中,探索完成,分别用 0,1,2代替,并且用一维向量visited存储状态,因为转化为有向图的时候prerequisites[1]才是出度边起始的节点,所以还需用一个edge的二维数组存储prerequisites[0] [1]交换后的位置,这样就可以得到每个节点的邻居结点,便于算法执行。之后便遍历每个节点并且判断是否为未探索并且不成环,是则DFS,否则如果不成环则继续下一个点,如果成环则直接return false。进入DFS后,先令该点为探索中的状态,在遍历该点的邻接点,判断是否为0,是则继续DFS,否则是探索中的状态则判断为成环,此时就return一直结束DFS,并且直接return false,否则是探索完成则换另一个邻居点继续DFS。

因为我们只需要判断是否存在这样一个拓扑排序,所以并不需要一个栈去存储拓扑排序的结果

代码:

 1 class Solution {
 2 public:
 3     vector<vector<int>> edge;
 4     vector<int> visited;
 5     bool valid=true;
 6 
 7     void dfs(int u){
 8         visited[u]=1;
 9         for(int v:edge[u]){
10             if(visited[v]==0){
11                 dfs(v);
12                 if(!valid) return;
13             }
14             else if(visited[v]==1){
15                 valid=false;
16                 return;
17             }
18         }
19         visited[u]=2;
20     }
21 
22     bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
23         edge.resize(numCourses);
24         visited.resize(numCourses);
25         for(const auto& exc:prerequisites){
26             edge[exc[1]].push_back(exc[0]);   //构建边的结构,存储一个结点,他的邻居结点是存于嵌套vector中
27         }
28         for(int i=0;i<numCourses&&valid;++i){
29             if(!visited[i])
30                 dfs(i);
31         }
32         return valid;
33     }
34 };

 

BFS:如果要使用BFS,首先就要解决如何变型传统的BFS使其具有判断成环的功能,这里考虑的是搜索入度边为0的结点,只把入度边为0的结点加入到队列,每次搜索一个邻居节点就把该邻居结点的这条入度边删除,即indeg[i] --,只要删除入度边后该结点入度边为0就加入进队列。如果没有入度边为0,那么就会存在环。同时我们计数探索过的节点数,只要这个数等于课程数,那么就能返回true 否则返回false

 

代码:

 1 class Solution {
 2 public:
 3     vector<vector<int>> edge;
 4     vector<int> indeg;
 5     bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
 6         edge.resize(numCourses);
 7         indeg.resize(numCourses);
 8         for(const auto& v:prerequisites){
 9             edge[v[1]].push_back(v[0]);  
10             ++indeg[v[0]];   //给每个结点计数入度边
11         }
12         queue<int> q;
13         for(int i=0;i<numCourses;++i){  //第一次将入度边为0的结点加入进队列
14             if(indeg[i]==0)
15                 q.push(i);
16         }
17         int visited=0; //用来计数探索过的节点数
18         while(!q.empty()){
19             ++visited;
20             int temp=q.front();
21             q.pop();
22             for(auto& u:edge[temp]){  //每次探索一个邻居结点
23                 --indeg[u];       //并减掉该入度边
24                 if(indeg[u]==0)    //只要该点入度边为0就加入队列
25                     q.push(u);
26             }
27         }
28         return visited == numCourses;
29     }
30 };

 

posted @ 2021-02-28 15:31  Mrsdwang  阅读(43)  评论(0)    收藏  举报