zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    命令行程序如何获取HINSTANCE?
    解决C++项目使用sqlite中文乱码问题
    第三章 CLR如何解析引用类型
    第二章 生成、打包、部署和管理应用程序及类型
    第一章 CLR执行模型
    如何快速提升自己硬实力
    前端优化
    Eureka的工作原理以及它与ZooKeeper的区别
    单链表反转
    链表中head->next = p;和p=head->next;之间的区别
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4977294.html
Copyright © 2011-2022 走看看