210. Course Schedule II

题目:

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, return the ordering of courses you should take to finish all courses.

There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.

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 the correct course order is [0,1]

4, [[1,0],[2,0],[3,1],[3,2]]

There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is[0,2,1,3].

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 the topological order 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.

链接: http://leetcode.com/problems/course-schedule-ii/

题解:

跟Course Schedule一样,这次是求出拓扑排序后的顺序。我们依然是用Kahn's Algorithm和Tarjan's Algorithm。

Kahn's Algorithm: 需要注意输出的先后顺序,edge [0,1]表示假如要take 0,必须先take 1,那么edge[0]也就是0其实inDegree = 1,而edge[1]也就是1的indegree = 0。这个edge等价于"1 -> 0"。

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {     // Kahn's Algorithms
        if(numCourses <= 0)
            return new int[]{};
        int[] res = new int[numCourses];
        for(int i = 0; i < numCourses; i++)
            res[i] = i;
            
        if(prerequisites == null || prerequisites.length == 0)
            return res;
        
        int[] inDegree = new int[numCourses];
        for(int[] edge : prerequisites)
            inDegree[edge[0]]++;
        
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 0; i < numCourses; i++)         // calculate inDegree
            if(inDegree[i] == 0)
                queue.offer(i);
        
        int index = 0;
        while(!queue.isEmpty()) {
            int source = queue.poll();
            res[index++] = source;              // reverse post order
            for(int[] edge : prerequisites) {
                if(edge[1] == source) {
                    inDegree[edge[0]]--;
                    if(inDegree[edge[0]] == 0)
                        queue.offer(edge[0]);
                }
            }
        }
        
        if(index == numCourses) {             //looped through all vertex
            return res;
        } else
            return new int[]{};     // has cycle, not DAG
    }
}

 

Tarjan's Algorithm:  也是使用跟Course Schedule I的方法,注意要有一个stack保存reverse post顺序。虽然能ac,但是速度很慢, 二刷要注意复杂度的问题,以及图的表示,从list of edges到Ajacency Matrix和Ajacency List的互相转换。以及Sparse Matrix如何优化等等。

Time Complexity - O(VE),Space Complexity - O(V)。

public class Solution {
    private boolean[] marked;           // mark visited vertex
    private boolean[] onStack;          // mark temp visited vertex for dfs
    private Stack<Integer> reversePost; // store topological ordering vertex
    private boolean result = true;
    
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] res = new int[numCourses];
        for(int i = 0; i < numCourses; i++)
            res[i] = i;
        if(prerequisites == null || prerequisites.length == 0)
            return res;
        this.marked = new boolean[numCourses];
        this.onStack = new boolean[numCourses];
        this.reversePost = new Stack<>();
        
        for(int v = 0; v < numCourses; v++) {
            if(!this.result)                // if found cycle
                return new int[]{};
            if(!marked[v]) 
                dfs(v, prerequisites);
        }
        
        int index = 0;
        while(!reversePost.isEmpty()) {
            res[index++] = reversePost.pop();
        }
        
        return res;
        
    }
    
    private void dfs(int v, int[][] prerequisites) {
        onStack[v] = true;      // temporarily mark this vertex = true on this dfs route
        marked[v] = true;       // permanently mark this vertex visited
        for(int[] edge : prerequisites) {
            if(edge[1] == v) {
                if(!marked[edge[0]])
                    dfs(edge[0], prerequisites);
                else {
                    if(onStack[edge[0]])
                        this.result = false;
                }
            }
        }
        
        onStack[v] = false;     // back-tracking
        reversePost.push(v);    // push vertex to reversePost stack
    }
}

 

二刷:

和 207题一样, 以后要注意转为 Adjacency Lists表示方法进行计算。

Java:

Kahn's Method

 

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses <= 0 || prerequisites == null) return new int[] {};
        int[] inDegree = new int[numCourses];
        for (int[] prerequisite : prerequisites) inDegree[prerequisite[0]]++;
        
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) q.offer(i);
        }
        int[] res = new int[numCourses];
        int index = 0;
        while (!q.isEmpty()) {
            int num = q.poll();
            res[index++] = num;
            for (int[] prerequisite : prerequisites) {
                if (prerequisite[1] == num) {
                    inDegree[prerequisite[0]]--;
                    if (inDegree[prerequisite[0]] == 0) {
                        q.offer(prerequisite[0]);
                    }
                }
            }
        }
        return index == numCourses ? res : new int[] {};
    }
}

 

 

Tarjan's method:

速度非常慢,下次再优化

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses <= 0 || prerequisites == null) return new int[] {};
        boolean[] visited = new boolean[numCourses];
        boolean[] onVisiting = new boolean[numCourses];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < numCourses; i++) {
            if (!dfs(i, prerequisites, visited, onVisiting, stack)) return new int[] {};
        }
        int[] res = new int[numCourses];
        for (int i = numCourses - 1; i >= 0; i--) res[i] = stack.pop();
        
        return res;
    }
    
    private boolean dfs(int i, int[][] prerequisites, boolean[] visited, boolean[] onVisiting, Stack<Integer> stack) {
        if (visited[i]) return true;
        visited[i] = true;
        onVisiting[i] = true;
        
        for (int[] prerequisite : prerequisites) {
            if (prerequisite[0] == i) {
                if (onVisiting[prerequisite[1]]) return false;
                if (!dfs(prerequisite[1], prerequisites, visited, onVisiting, stack)) return false;
            }
        }
        
        onVisiting[i] = false;
        stack.push(i);
        return true;
    }
}

 

优化后:

Kahn's Method - BFS using Graph as Adjacency Lists

Time Complexity - O(V + E),Space Complexity - O(V)。

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses <= 0 || prerequisites == null) return new int[] {};
        int[] inDegree = new int[numCourses];
        List<List<Integer>> graph = new ArrayList<>();
        
        for (int i = 0; i < numCourses; i++) graph.add(new ArrayList<Integer>());
        for (int i = 0; i < prerequisites.length; i++) {
            inDegree[prerequisites[i][0]]++;
            graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
        }
        
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) q.offer(i);
        }
        int[] res = new int[numCourses];
        int index = 0;
        while (!q.isEmpty()) {
            int num = q.poll();
            res[index++] = num;
            for (int i : graph.get(num)) {
                inDegree[i]--;
                if (inDegree[i] == 0) {
                    q.offer(i);
                }
            }
        }
        return index == numCourses ? res : new int[] {};
    }
}

 

Tarjan's Method - BFS using Graph as Adjacency Lists

Time Complexity - O(V + E),Space Complexity - O(V)。

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses <= 0 || prerequisites == null) return new int[] {};
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) graph.add(new ArrayList<Integer>());
        for (int i = 0; i < prerequisites.length; i++) graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
        
        boolean[] visited = new boolean[numCourses];
        boolean[] onVisiting = new boolean[numCourses];
        Stack<Integer> stack = new Stack<>();
        
        for (int i = 0; i < numCourses; i++) {
            if (!dfs(i, graph, visited, onVisiting, stack)) return new int[] {};
        }
        
        int[] res = new int[numCourses];
        for (int i = 0; i < numCourses; i++) res[i] = stack.pop();
        return res;
    }
    
    private boolean dfs(int num, List<List<Integer>> graph, boolean[] visited, boolean[] onVisiting, Stack<Integer> stack) {
        if (visited[num]) return true;
        visited[num] = true;
        onVisiting[num] = true;
        for (int i : graph.get(num)) {
            if (onVisiting[i]) return false;
            if (!dfs(i, graph, visited, onVisiting, stack)) return false;
        }
        onVisiting[num] = false;
        stack.push(num);
        return true;
    }
}

 

三刷:

Java:

BFS:

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses < 0 || prerequisites == null) return new int[] {};
        List<List<Integer>> adjListsGraph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) adjListsGraph.add(new ArrayList<>());
        int[] inDegrees = new int[numCourses];
        
        for (int[] prerequisite : prerequisites) {
            adjListsGraph.get(prerequisite[1]).add(prerequisite[0]);
            inDegrees[prerequisite[0]]++;
        }
        
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegrees[i] == 0) q.offer(i);
        }
        
        int[] res = new int[numCourses];
        int idx = 0;
        while (!q.isEmpty()) {
            int course = q.poll();
            res[idx++] = course;
            for (int dependent : adjListsGraph.get(course)) {
                inDegrees[dependent]--;
                if (inDegrees[dependent] == 0) q.offer(dependent);
            }
        }
        return (idx == numCourses) ? res : new int[] {}; 
     }
}

 

DFS:

public class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses < 0 || prerequisites == null) return new int[] {};
        List<List<Integer>> adjListsGraph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) adjListsGraph.add(new ArrayList<>());
        for (int[] prerequisite : prerequisites) adjListsGraph.get(prerequisite[1]).add(prerequisite[0]);
        boolean[] visited = new boolean[numCourses];
        boolean[] onVisitingPath = new boolean[numCourses];
        Stack<Integer> stack = new Stack<>();
        
        for (int i = 0; i < numCourses; i++) {
            if (!visited[i] && !canFindOrder(i, adjListsGraph, visited, onVisitingPath, stack)) return new int[] {};
        }
        
        int[] res = new int[numCourses];
        for (int i = 0; i < numCourses; i++) res[i] = stack.pop();
        return res;
     }
     
     private boolean canFindOrder(int course, List<List<Integer>> adjListsGraph, boolean[] visited, boolean[] onVisitingPath, Stack<Integer> stack) {
         if (visited[course]) return true;
         onVisitingPath[course] = true;
         for (int dependent : adjListsGraph.get(course)) {
             if (onVisitingPath[dependent] || !canFindOrder(dependent, adjListsGraph, visited, onVisitingPath, stack)) {
                 return false;
             }
         }
         onVisitingPath[course] = false;
         visited[course] = true;
         stack.push(course);
         return true;
     }
}

 

Reference:

http://algs4.cs.princeton.edu/42digraph/

207. Course Schedule

posted @ 2015-11-19 13:09  YRB  阅读(1387)  评论(0编辑  收藏  举报