码迷,mamicode.com
首页 > 其他好文 > 详细

Course Schedule

时间:2015-06-25 15:23:01      阅读:115      评论:0      收藏:0      [点我收藏+]

标签:

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

Course Schedule

标签:

原文地址:http://www.cnblogs.com/NickyYe/p/4588555.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!