LeetCode | 207. 课程表

原题Medium):

  现在你总共有 n 门课需要选,记为 0 到 n-1。

  在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

  给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?

  

说明:

  输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
  你可以假定输入的先决条件中没有重复的边。
提示:

  这个问题相当于查找一个循环是否存在于有向图中。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。
  拓扑排序也可以通过 BFS 完成。

 

思路:拓扑排序

  只看题可能不太容易理解题意,结合给出的说明和提示,我们应该明白这题考的就是给出一个有向图的节点数和边集,查找该有向图是否有环。建立拓扑排序首先肯定就是从入度为0的节点开始,然后把其指向的节点的入度减一,如果减一后有节点的入度归零,那么就把入度归零的节点放进拓扑排序里。举一个有向图经拓扑排序后的图:

   我不想在此过多讨论拓扑排序的概念。我只想讨论如何实现拓扑排序。题目给予的资源是节点数和边集,为了实现拓扑排序,我们需要一个邻接链表和入度数组,根据边集,我们可以构建图的邻接链表和入度数组。具体实现可以是:定义一个map容器,里面存放每个节点出度点的集合,集合可用set容器表示。在遍历边集中,每一条边u→v,把v插入至key为u的set集合里,再把v的入度+1。在构建完邻接链表和入度数组后,可以从入度为零的节点出发,使用BFS来遍历整个图,BFS一般使用队列实现,所以先把入度为零的节点放进队列,然后在队列中按FIFO顺序把这些入度为零的点弹出,在邻接链表里找到它的出度点集合,把这些节点的入度数减一,减一之后查看这些节点的入度数是否归零,归零就放入队列,直到队列为空,就完成对图的拓扑排序。我们可以使用一个整型变量来记录从队列中弹出的节点的个数,每弹出一个节点就+1,如果图是有环的情况,那么所记录到的节点数就会与提供的节点数不一致。

 1     bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
 2         //邻接链表,set集合为的是帮其所指的节点按先后自动排序
 3         map<int,set<int>> adjacent;
 4         //入度数组
 5         vector<int>indegree(numCourses);
 6         
 7         //遍历边集,对于边集中的每条边来说,u→v
 8         for(auto& edge : prerequisites)
 9         {
10             int u = edge[0];
11             int v = edge[1];
12             
13             adjacent[u].insert(v);      //把v插入至key为u的set集合里
14             indegree[v]++;              //再把v的入度+1
15         }
16         
17         int count = 0;
18         queue<int> indeQ;
19         //拓扑排序的起点就是入度为0的节点,所以先把入度为0的节点放进队列中
20         for(int i = 0; i<numCourses; i++)
21             if(!indegree[i]) indeQ.push(i);
22         
23         //在队列中按FIFO顺序弹出入度为0的结点,根据邻接链表把该节点所指的所有节点的入度数-1,如果有节点在-1之后入度数为0,放进队列中
24         while(!indeQ.empty())
25         {
26             int node = indeQ.front();
27             indeQ.pop();
28             //每在队列中弹出一个节点,说明需要学习的课程+1
29             count++;
30             //根据邻接链表把该节点所指的所有节点(是个set集合)
31             auto& adjs = adjacent[node];
32             //入度数都-1
33             for(auto adj : adjs)
34             {
35                 indegree[adj]--;
36                 //如果有节点在-1之后入度数为0,放进队列中
37                 if(!indegree[adj]) indeQ.push(adj);
38             }
39         }
40         //如果需要学习的课程数与课程总量不想等,说明存在先决条件成环
41         return count == numCourses;
42     }

posted @ 2019-10-31 15:08  羽园原华  阅读(185)  评论(0编辑  收藏  举报