【LeetCode】Course Schedule--拓扑排序

来源:互联网 发布:淘宝天猫客服电话 编辑:程序博客网 时间:2024/06/05 06:17


【题目】

There are a total of n courses you have to take, labeled from 0 to n - 1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

For example:

2, [[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.

分析:这道题主要是看课程之间是否存在相互依赖关系,从图论角度见是否构成环。 
环的入度永远不会为0,因此,本题的思想是先找出入度为0的点,然后加入到list中,然后从list中弹出,获取元素的相临节点(通过map),然后将对应节点的入度–,当为0时,加入到list中。 
当list为空时,此时如果还有num不为-1,未处理,那么就表示课程之间存在环。返回false;

代码如下:400ms

public class Solution {    public boolean canFinish(int numCourses, int[][] prerequisites) {        HashMap<Integer,List<Integer>> maps = new HashMap<>();        int[] indegree = new int[numCourses];        int length = prerequisites.length;        if(length<=0)            return true;        //init Map        for(int i = 0;i<length;i++){            int first = prerequisites[i][0];            int second = prerequisites[i][1];            if(!maps.containsKey(first))                maps.put(first,new LinkedList<>());            maps.get(first).add(second);            indegree[second]++;        }        List<Integer> list = new LinkedList();        for(int i = 0;i<numCourses;i++){            if(indegree[i]==0)                list.add(i);        }        while(list.size()>0){            int val = list.get(0);            indegree[val] = -1;            list.remove(0);            if(!maps.containsKey(val))                continue;            List<Integer> tmpList = maps.get(val);            for(int i = 0;i<tmpList.size();i++){                int index = tmpList.get(i);                indegree[index]--;                if(indegree[index]==0)                    list.add(index);            }        }        for(int i = 0;i<numCourses;i++)            if(indegree[i]!=-1)                return false;        return true;    }}


【解析】

典型的拓扑排序。原理也很简单,在一个有向图中,每次找到一个没有前驱节点的节点(也就是入度为0的节点),然后把它指向其他节点的边都去掉,重复这个过程(BFS),直到所有节点已被找到,或者没有符合条件的节点(如果图中有环存在)。

回顾一下图的三种表示方式:边表示法(即题目中表示方法),邻接表法,邻接矩阵。用邻接表存储图比较方便寻找入度为0的节点。

【Java代码】

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  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.           
  24.         // remove a non-pre course each time  
  25.         for (int i = 0; i < numCourses; i++) {  
  26.             // find a non-pre course  
  27.             int j = 0;  
  28.             for ( ; j < numCourses; j++) {  
  29.                 if (preNums[j] == 0break;  
  30.             }  
  31.               
  32.             // if not find a non-pre course  
  33.             if (j == numCourses) return false;  
  34.               
  35.             preNums[j] = -1;  
  36.               
  37.             // decrease courses that post the course  
  38.             Set set = posts.get(j);  
  39.             Iterator<Integer> it = set.iterator();  
  40.             while (it.hasNext()) {  
  41.                 preNums[it.next()]--;  
  42.             }  
  43.         }  
  44.           
  45.         return true;  
  46.     }  
  47. }  

注意,输入可能有重复的边,所以邻接表用HashSet存储。

下面一种代码是不用HashSet的,对于重复的边,它在邻接表中村了两份,同时计算入度时也算了两次,所以代码不会有问题。但个人感觉最好用HashSet,这样符合图的定义。

下面的代码还是比较典型的BFS写法,大家可以对比理解下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class Solution {  
  2.     public boolean canFinish(int numCourses, int[][] prerequisites) {  
  3.         List<List<Integer>> posts = new ArrayList<List<Integer>>();  
  4.         for (int i = 0; i < numCourses; i++) {  
  5.             posts.add(new ArrayList<Integer>());  
  6.         }  
  7.           
  8.         int[] preNums = new int[numCourses];  
  9.         for (int i = 0; i < prerequisites.length; i++) {  
  10.             posts.get(prerequisites[i][1]).add(prerequisites[i][0]);  
  11.             preNums[prerequisites[i][0]]++;  
  12.         }  
  13.           
  14.         Queue<Integer> queue = new LinkedList<Integer>();  
  15.         for (int i = 0; i < numCourses; i++) {  
  16.             if (preNums[i] == 0){  
  17.                 queue.offer(i);  
  18.             }  
  19.         }  
  20.           
  21.         int count = numCourses;  
  22.         while (!queue.isEmpty()) {  
  23.             int cur = queue.poll();  
  24.             for (int i : posts.get(cur)) {  
  25.                 if (--preNums[i] == 0) {  
  26.                     queue.offer(i);  
  27.                 }  
  28.             }  
  29.             count--;  
  30.         }  
  31.           
  32.         return count == 0;  
  33.     }  
  34. }  

题解:

topological sort. 每一门课就是一个vertex, 每一个preRequest就是一个edge.

求Course Schedule,等同问题是有向图检测环. 一个DAG的Topological Order可以有大于1种。

常用的Topological Sorting算法有两种

  1. Kahn's Algorithms (wiki): BFS based, start from with vertices with 0 incoming edge,insert them into list S,at the same time we remove all their outgoing edges,after that find new vertices with 0 incoming edges and go on. 
  2. Tarjan's Algorithms (wiki): DFS based, loop through each node of the graph in an arbitrary order,initiating a depth-first search that terminates when it hits any node that has already been visited since the beginning of the topological sort or the node has no outgoing edges (i.e. a leaf node). 

Time Complexity: O(V + E). Space: O(V).

AC Java:

复制代码
 1 public class Solution { 2     public boolean canFinish(int numCourses, int[][] prerequisites) { 3         //BFS based 4         if(numCourses <= 0){ 5             return true; 6         } 7          8         //用List<List>建立 adjancy list 9         List<List<Integer>> fromTo = new ArrayList<List<Integer>>();10         for(int i = 0; i<numCourses; i++){11             fromTo.add(new ArrayList<Integer>());12         }13         for(int [] edge: prerequisites){14             fromTo.get(edge[1]).add(edge[0]);15         }16         17         //计算每门课的indegree18         int [] inDegree = new int[numCourses];19         for(int [] edge : prerequisites){20             inDegree[edge[0]]++;21         }22         23         //把indegree为0的课加到queue里面24         List<Integer> res = new ArrayList<Integer>();25         LinkedList<Integer> que = new LinkedList<Integer>();26         for(int i = 0; i<inDegree.length; i++){27             if(inDegree[i] == 0){28                 que.add(i);29             }30         }31         32         //从queue里poll出来的课程 去掉他们的outdegree edge33         while(!que.isEmpty()){34             int source = que.poll();35             res.add(source);36             37             for(int destination: fromTo.get(source)){38                 inDegree[destination]--;39                 //若是终点的indegree减一后变成0, 就加到queue中.40                 if(inDegree[destination] == 0){41                     que.add(destination);42                 }43             }44         }45         return res.size() == numCourses;46     }47 }
复制代码
0 0
原创粉丝点击