[LeeCode] 207. Course Schedule Java

题目:

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.

题意及分析:课程之间有依赖关系,问是否能安排课程表。从图论的角度来看就是:有向图是否存在环,存在就false,否则就返回true。最开始我想到的是暴力求解,直接对每个点查找是否能返回到该点,若有则直接返回false,最后超时。判断一个有向图是否有环,可以使用拓扑排序:

一个 DAG 图,那么如何写出它的拓扑排序呢?这里说一种比较常用的方法:

 

  1. 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
  2. 从图中删除该顶点和所有以它为起点的有向边。
  3. 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

 

超时代码:

public class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int row = prerequisites.length;
        if(row==0) return true;
        HashMap<Integer,List<Integer>> hashMap = new HashMap<>();       //记录每个出发点及其能到达的点,list为学习该门课程需要学习的先序课程
        for(int i=0;i<row;i++){
            int[] edge = prerequisites[i];
            int begin = edge[0];        //课程
            int end = edge[1];          //需要的先序课程
            if(hashMap.containsKey(begin)){
                hashMap.get(begin).add(end);
            }else{
                List<Integer> temp = new ArrayList<>();
                temp.add(end);
                hashMap.put(begin,temp);
            }
        }

        Queue<Integer> tempQueue = new LinkedList<>();      //用来进行宽度遍历,需要弹出的

        Iterator iter = hashMap.entrySet().iterator();
        while(iter.hasNext()){
            Queue<Integer> queue = new LinkedList<>();      //记录从该点开始被遍历过的点
            Map.Entry entry = (Map.Entry) iter.next();
            int key = (Integer) entry.getKey();
            tempQueue.add(key);         //该队列用来对该点就行宽度遍历查找
            while(!tempQueue.isEmpty()){
                int num = tempQueue.poll();
                List<Integer> neighbor = hashMap.get(num);   //该点能到达的点
                if(!queue.contains(num)){       //该点未被遍历过
                    queue.add(num);
                    if(neighbor!=null){     //若有该点能到达的点
                        for(int i=0;i<neighbor.size();i++){
                            if(key==(neighbor.get(i))) {     //包含循环,又回到起始点
                                return false;
                            }else {
//                                queue.add(neighbor.get(i));
                                tempQueue.add(neighbor.get(i));
                            }
                        }
                    }
                }
            }

        }
        return true;
    }
}

 拓扑排序代码:

public class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] map = new int[numCourses];        //记录每个点的入度

        for(int i=0;i<prerequisites.length;i++){      //计算入度
            map[prerequisites[i][1]]++;
        }

        Queue<Integer> queue = new LinkedList<>();      //保存没有前驱的点,即入度为0的点
        for(int i=0;i<map.length;i++){
            if(map[i]==0) queue.add(i);
        }

        int count = queue.size();     //用来记录被删除的点的个数,若和课程数相等则可以排出课程
        while (!queue.isEmpty()){
            int key = queue.poll();     //删除的课程,并将该课程的所能达到的所有点的入度减1
            for(int i=0;i<prerequisites.length;i++){
                if(key==prerequisites[i][0]){
                    int l = prerequisites[i][1];
                    map[l]--;       //入度减1
                    if(map[l]==0){  //入度为0,添加进queue
                        queue.add(l);
                        count++;
                    }
                }
            }
        }
        return count==numCourses;
    }
}

 

posted @ 2017-07-27 10:43  荒野第一快递员  阅读(295)  评论(0编辑  收藏  举报