Course Schedule

https://leetcode.com/problems/course-schedule/

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.

Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.

click to show more hints.

Hints:
    1. This problem is equivalent to finding if a cycle exists in a directed graph. If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses.
    2. Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort.
    3. Topological sort could also be done via BFS.

解题思路:

本题看上去就很像图的题目,但是又略有不同。因为这个图,是有向的。一个[1,0]的edge,表示上1之前必须上0,实际上就是一个由顶点0指向顶点1的edge。

我们可以观察,对于课程x,选择它,就必须先上n门课,那么x的入度就是n。相反,它是选择m门课的前提,那么x的出度就是m。

这道题的问题,能不能finish all courses的充要条件其实是,这个有向图有无环。

下面是一个BFS的解法。

1. 统计出每门课的入度。

2. 将入度为0,也就是可以随意先上的课,加入到队列中。count++。

3. 取出队列头的课程y,对于它的每个出度课程x,如果x的入度已经为0,则将x加入队列。count++。

4. 如果x出度不为0,将其出度减一。可以理解为因为y已经上过了,所以x的前置课程少了一门。

5. 循环执行3-4,直到队列为空。

7. 看count是不是等于所有课程数量,等于代表所有课程都finish,返回true,否则返回false。

public class Solution {

    public boolean canFinish(int numCourses, int[][] prerequisites) {

        Queue<Integer> queue = new LinkedList<Integer>();

        // 课程index的前置课程数量

        int[] preNum = new int[numCourses];

        int count = 0;

        for(int[] pair : prerequisites) {

            preNum[pair[0]]++;

        }

        for(int i = 0; i < numCourses; i++) {

            if(preNum[i] == 0) {

                queue.offer(i);

                count++;

            }

        }

        while(queue.size() > 0) {

            int num = queue.poll();

            for(int[] pair : prerequisites) {

                if(pair[1] == num) {

                    preNum[pair[0]]--;//上了一节前置课程

                    if(preNum[pair[0]] == 0) {//只有当前置课程全部上了,该才能上该课程

                        queue.offer(pair[0]);

                        count++;

                    }

                }

            }

        }

        return count == numCourses;

    }

}

下面是一个DFS的解法,与BFS不同,DFS建立的map是看每个节点的出度。

L ← Empty list that will contain the sorted nodes

while there are unmarked nodes do

    select an unmarked node n

    visit(n) 

function visit(node n)

    if n has a temporary mark then stop (not a DAG)

    if n is not marked (i.e. has not been visited yet) then

        mark n temporarily

        for each node m with an edge from n to m do

            visit(m)

        mark n permanently

        unmark n temporarily

        add n to head of L

上面是wiki的伪代码,L是最后的拓扑排序结果

1. 对于所有的节点,调用dfs方法。

2. 对于当前节点n,如果n有一个暂时标记,证明n正在被遍历,有回环,停止,不可排序。

3. 如果n啥标记没,证明还没被遍历过,那么标记n为暂时,正在遍历。

4. 对于n的所有出度,循环执行2-3步。

5. 遍历完n的所有出度后,将n标记为已遍历,取消正在遍历的标志。并将n加入排序结果L。

public class Solution {

    public boolean canFinish(int numCourses, int[][] prerequisites) {

        int[] visited = new int[numCourses];

        Map<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();

        //map记录的是key的出度

        for(int[] pair : prerequisites) {

            if(map.get(pair[1]) == null) {

                List<Integer> res = new ArrayList<Integer>();

                res.add(pair[0]);

                map.put(pair[1], res);

            } else {

                List<Integer> res = map.get(pair[1]);

                res.add(pair[0]);

            }

        }

        for(int i = 0; i < numCourses; i++) {

            //有一个顶点不能被遍历到,就返回false

            if(!dfs(map, visited, i)) {

                return false;

            }

        }

        return true;

    }

    

    public boolean dfs(Map<Integer, List<Integer>> map, int[] visited, int step) {

        // -1 已经遍历过,0为遍历过,1正在被遍历

        //下一顶点仍然在被遍历,有环了,就返回false

        if(visited[step] == 1) {

            return false;

        }

        //下一顶点已经遍历过

        if(visited[step] == -1) {

            return true;

        }

        List<Integer> list = map.get(step);

        visited[step] = 1;

        if(list == null) {

            visited[step] = -1;

            return true;

        }

        for(Integer next : list) {

            // 有一个顶点不可达(环),就返回false

            if(!dfs(map, visited, next)) {

                return false;

            }

        }

        //标记当前顶点为已遍历过

        visited[step] = -1;

        return true;

    }

}

 

http://blog.csdn.net/dm_vincent/article/details/7714519

http://www.programcreek.com/2014/05/leetcode-course-schedule-java/

https://en.wikipedia.org/wiki/Topological_sorting

你可能感兴趣的:(schedule)