Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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,将其入度减一。可以理解为因为y已经上过了,所以x的前置课程少了一门。

  如果此时x的入度已经为0,则将x加入队列。count++。

  如果x入度仍不为0,代表x还有前置课程没上,啥也不做。

4. 不断执行3,直到队列为空。

5. 看count是不是等于所有课程数量,等于代表所有课程都上过了,返回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

// 20181016

一个简单一点的dfs版本,实际上遍历完成的-1的状态并不是每次都需要置上

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] visited = new int[numCourses];
        Map<Integer, List<Integer>> nextMap = new HashMap<Integer, List<Integer>>();
        
        for (int[] pair : prerequisites) {
            if (nextMap.containsKey(pair[1])) {
                nextMap.get(pair[1]).add(pair[0]);
            } else {
                List<Integer> list = new ArrayList<Integer>();
                list.add(pair[0]);
                nextMap.put(pair[1], list);
            }
        }
        
        for (int i = 0; i < numCourses; i++) {
            if (!dfs(nextMap, visited, i)) {
                return false;
            }
        }
        return true;
    }
    
    // visited:
    // 1 正在被遍历
    // 0 还没遍历到
    // -1 遍历已经结束
    public boolean dfs(Map<Integer, List<Integer>> map, int[] visited, int course) {
        if (visited[course] == 1) {
            return false;
        } else {
            visited[course] = 1;
        }
        
        if (map.containsKey(course))
        {
            List<Integer> nextCourses = map.get(course);        
            for (int next : nextCourses) {            
                if(!dfs(map, visited, next)) {
                    return false;
                }            
            }
        }        
        visited[course] = -1;
        return true;
    }
}

 

posted on 2015-06-25 14:55  NickyYe  阅读(264)  评论(0编辑  收藏  举报