Leetcode207--->课程表(逆拓扑排序)

题目: 课程表,有n个课程,[0, n-1];在修一个课程前,有可能要修前导课程;

举例:

2, [[1,0]]  修课程1前需要先修课程0

There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.

2, [[1,0],[0,1]]  

There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

解题思路:

其本质是逆拓扑排序,因此首先要将给定的课程关系转换为邻接表存储,即将给定的图转化为邻接表;

举例说明:

4, [[1,0],[2,0],[3,1],[3,2]]

1) 代码5的for循环是为了初始化一个邻接链表的结构,[[],[],[],[],]
2) 代码10的for循环是为了找到有那几个节点要依靠当前节点;其中prerequisites[0][1] = 0,prerequisites[0][0] = 1,表示课程1要依靠课程0;prerequisites[1][1] = 0,prerequisites[1][0] = 2,表示课程2依靠课程0;以此类推,则最终posts中的结果为:
[[1,2],[3],[3],[]]表示课程1,2依靠课程0,课程3依靠课程1,课程3依靠课程2,没有课程依靠课程3;
3) 代码15声明了一个数组preNums,下面代码16的for循环来告诉我们这个数组是干什么的;
4) 代码16的for循环中,拿出post中的每一个set,假设此时拿出的是post.get(0),则拿出的是依靠0课程才能修完课程的集合[1,2];该循环结束后,preNums=[0,1,1,2]表示0课程不依赖其他课程(0课程的出度为0),1课程依赖一个课程,2课程依赖一个课程,3课程依赖两个课程;因此preNums数组表示下标的课程依赖几个其他的课程
5) 到此为止,我们记录了下标课程被哪几个课程依赖,即posts;下标课程自己依赖几个课程,即preNums数组;到这里可以看出我们使用的逆拓扑结构的思想;即找出preNums数组中为0的那个下标课程(在图中表示出度为0),代码27行的for循环就是干这个事情的;
6) 假设此时没有找到出度为0的那个课程,则表示课程中有相互依赖的情况,则直接return false; 如果找到出度为0的那个课程,在此例子中为课程0,而此时课程0在post中可以得出其被[1,2]课程依赖,因此需要删除0节点,那么相应的也要删掉课程1和课程2分别对课程0的依赖,那此时,需要更新preNums数组,即1课程所依赖的课程数减一,
2课程所依赖的课程数减一,则此时preNums = [-1, 0, 0, 2],-1表示已经删除了0节点。循环5,6)则可以判断中课程是否可以顺利修完,即给出的数组是否是一个逆拓扑结构;
 1 public class Solution {
 2     public boolean canFinish(int numCourses, int[][] prerequisites) {
 3         // init the adjacency list
 4         List<Set> posts = new ArrayList<Set>();
 5         for (int i = 0; i < numCourses; i++) {
 6             posts.add(new HashSet<Integer>());
 7         }
 8 
 9         // fill the adjacency list,找到有哪几个点要依靠该点
10         for (int i = 0; i < prerequisites.length; i++) {
11             posts.get(prerequisites[i][1]).add(prerequisites[i][0]);
12         }
13 
14         // count the pre-courses
15         int[] preNums = new int[numCourses];  // 计算下标的课程依赖几个课程
16         for (int i = 0; i < numCourses; i++) {
17             Set set = posts.get(i);
18             Iterator<Integer> it = set.iterator();
19             while (it.hasNext()) {
20                 preNums[it.next()]++;
21             }
22         }
23         // remove a non-pre course each time
24         for (int i = 0; i < numCourses; i++) {
25             // find a non-pre course
26             int j = 0;
27             for ( ; j < numCourses; j++) {   // 找到出度为0的点,删掉,并更新依靠该点的前驱点的个数
28                 if (preNums[j] == 0) break;
29             }
30 
31             // if not find a non-pre course
32             if (j == numCourses) return false;
33 
34             preNums[j] = -1;
35 
36             // decrease courses that post the course
37             Set set = posts.get(j);
38             Iterator<Integer> it = set.iterator();
39             while (it.hasNext()) {
40                 preNums[it.next()]--;    // 删除依赖j节点的节点的出度
41             }
42         }
43 
44         return true;
45     }
46 }

 



posted @ 2016-09-21 15:49  小叶子leavescy  阅读(2028)  评论(0编辑  收藏  举报