Leetcode 207. Course Schedule & 210. Course Schedule II

来源:互联网 发布:公交车刷卡软件 编辑:程序博客网 时间:2024/05/18 03:38

207. Course Schedule

Total Accepted: 52880 Total Submissions: 182222 Difficulty: Medium

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.

思路:

解法一:

自己写的。新建了一个class myCourse,里面有:一个int类型的课程num,一个boolean类型的outputed来避免之后把输出过的元素重复输出,一个HashSet<Integer>来存放所有该门课之前的课程num。

这样先初始化一个myCourse类型的数组,长度为num,然后遍历一遍pre要求,把各个课程的pre课程加入各自的HashSet中。然后每次都遍历我们创建的数组,取出一个HashSet.size() = 0的点,并且修改收到影响的其他课程的HashSet的元素个数(减一)。

整个过程其实思路非常明白,缺点就是相比于BFS和DFS慢了很多。其他解法中有分析。

//  to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]public class Solution { // 135ms    public boolean canFinish(int num, int[][] pre) {        myCourse[] list = new myCourse[num];        for(int i = 0; i < list.length; i++){            list[i] = new myCourse(i);        }                for(int i = 0; i < pre.length; i++){            int[] item = pre[i];            list[item[0]].pre.add(item[1]);        }                while(num > 0){            int nextIndex = getNext(list);            if(nextIndex == -1) return false;            num--;        }        return true;    }        public int getNext(myCourse[] list){        for(int i = 0; i < list.length; i++){            if(list[i].outputed == false && list[i].pre.isEmpty()){                int res = list[i].number;                                for(int j = 0; j < list.length; j++){                    if(j == i) continue;                    if(list[j].pre.contains(res)) list[j].pre.remove(res);                }                                list[i].outputed = true;                return res;            }        }        return -1;    }        public class myCourse{        int number;        boolean outputed = false;        Set<Integer> pre = new HashSet<Integer>();         // Set<Integer> after = new HashSet<Integer>(); // for reverse topological sort purpose                myCourse(int num){            this.number = num;        }    }}

解法二:

Breadth-first search。这里的bfs和dfs都来源于这里,总结的非常好。接下来的代码均修改于原作者。

省去了list数组(因为我的解法中list的index其实和myCourse.num相符合),HashSet换成了int[]的数组来count数目,而不是记录具体那些课程是pre课程。自己写的时候觉得比如1是0的pre,那么1完了我要找到0这个课,然后修改。如果我只计数,我怎么知道0的pre里面如果只有一门课是1还是2?? 答:可以知道。因为int[][] pre取出来的item是一个二维int数组,那么就会是类似item[0][1]这样的形式。也就是说,我所担心的connection关系其实已经包含在input里面了,这里item就告诉了我们去修改0,所以利用这个关系可以直接将0的计数-1,而不是说去遍历0。

还有一个地方BFS快:每次count--之后,就直接判断是不是为0,如果是直接入队,这样就节省了每次从0~n-1去遍历找寻count=0的点的时间。

下面代码输出的思路是,queue里面的都是pre = 0的课程,每次有元素入队符合要求的课程数量++,最后队列为空也就是说没有符合条件的元素了,这样判断总共入队的数量和总共课程数量就知道是不是有环了。

Time Complexity - O(VE), Space Complexity - O(V)。删除了作者的res ArrayList。

public class Solution { // 61ms    public boolean canFinish(int numCourses, int[][] prerequisites) {   // Kahn's method        if (numCourses <= 0) return false;        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 count = 0;        while (!q.isEmpty()) {            int num = q.poll();            count++;            for (int[] prerequisite : prerequisites) {                if (prerequisite[1] == num) {                    inDegree[prerequisite[0]]--;                    if (inDegree[prerequisite[0]] == 0) {                        q.offer(prerequisite[0]);                    }                }            }        }        return count == numCourses;    }}

解法三:

Depth-first search。需要额外一个数组去判断当前这个vertex有没有被访问过。这样如果遍历的时候发现该节点访问过,说明有环返回false。难点是:visited数组要有3个状态:

没访问过的状态0,访问后并且畅通的状态1,已及当前正在访问的状态-1。于是先置-1,然后遍历本课程的pre课程,结束之后就把状态置为-1,说明本课程是畅通无阻的。或者使用俩boolean数组,前者更快,后者更明白。

Time Complexity - O(VE), Space Complexity - O(V)。 这里去除了原文作者的stack和stack操作;使用了visited数组。

public class Solution { // 69ms    public boolean canFinish(int numCourses, int[][] prerequisites) {        int[] visited = new int[numCourses];                for (int i = 0; i < numCourses; i++) {            if (!dfs(i, prerequisites, visited)) return false;        }                return true;    }        private boolean dfs(int i, int[][] prerequisites, int[] visited) {        if (visited[i] == 1) return true;        if (visited[i] == -1) return false;                visited[i] = -1;        for (int[] prerequisite : prerequisites) {            if (prerequisite[0] == i) {                if (!dfs(prerequisite[1], prerequisites, visited)) return false;            }        }        visited[i] = 1;        return true;    }}


原文作者表示,想要提高速度到O(V+E),必须把输入换成邻接表的形式。以下是原文作者的代码(有修改),俩代码都是Time Complexity - O(V + E), Space Complexity - O(V)。可以看到提升速度非常明显。

public class Solution { // 10ms    public boolean canFinish(int numCourses, int[][] prerequisites) {   // Kahn's method        if (numCourses <= 0 || prerequisites == null) return false;        if (prerequisites.length == 0) return true;        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 count = 0;        while (!q.isEmpty()) {            int num = q.poll();            count++;            for (int i : graph.get(num)) {                inDegree[i]--;                if (inDegree[i] == 0) {                    q.offer(i);                }            }        }        return count == numCourses;    }}

public class Solution { // 8ms    public boolean canFinish(int numCourses, int[][] prerequisites) {        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]);                int[] visited = new int[numCourses];                for (int i = 0; i < numCourses; i++) {            if (!dfs(i, graph, visited)) return false;        }        return true;    }        private boolean dfs(int i, List<List<Integer>> graph, int[] visited) {        if (visited[i] == 1) return true;        if (visited[i] == -1) return false;                visited[i] = -1;        for (int num : graph.get(i)) {            if (!dfs(num, graph, visited)) return false;        }        visited[i] = 1;        return true;    }}




210. Course Schedule II

Total Accepted: 36641 Total Submissions: 158353 Difficulty: Medium

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.

同样的相比于上面的题目,这个题也有三种解法。Course Schedule I 判断能不能,II求路径;word break I判断能不能break, II求组合。一样的类型。

解法一:

沿用自己的解法。加一个结果数组int[] res = new int[num]。把之前代码修改为每当找不到下一个符合要求的元素时,返回new int[0]就行了。

//  to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]public class Solution { // 132ms    public int[] findOrder(int num, int[][] pre) {        myCourse[] list = new myCourse[num];        int[] res = new int[num];                for(int i = 0; i < list.length; i++){            list[i] = new myCourse(i);        }        for(int i = 0; i < pre.length; i++){            int[] item = pre[i];            list[item[0]].pre.add(item[1]);        }                int index = 0;        while(index < num){            int nextIndex = getNext(list);            if(nextIndex == -1) return new int[0];            res[index++] = nextIndex;        }        return res;    }        public int getNext(myCourse[] list){        for(int i = 0; i < list.length; i++){            if(list[i].outputed == false && list[i].pre.isEmpty()){                int res = list[i].number;                                for(int j = 0; j < list.length; j++){                    if(j == i) continue;                    if(list[j].pre.contains(res)) list[j].pre.remove(res);                }                                list[i].outputed = true;                return res;            }        }        return -1;    }        public class myCourse{        int number;        boolean outputed = false;        Set<Integer> pre = new HashSet<Integer>();         // Set<Integer> after = new HashSet<Integer>(); // for reverse topological sort purpose                myCourse(int num){            this.number = num;        }    }}


BFS和DFS解法我把上面作者的解法自己写了一遍,如果您需要原文210的文章链接,点击这里。

解法二:

BFS:

public class Solution { // 66ms    public int[] findOrder(int numCourses, int[][] prerequisites) {        if (numCourses <= 0) return new int[0];        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 == res.length ? res : new int[0];    }}


解法三:

DFS:

public class Solution { // 63ms    public int[] findOrder(int numCourses, int[][] prerequisites) {        if (numCourses <= 0) return new int[0];        int[] visited = new int[numCourses];        Stack<Integer> stack = new Stack<Integer>();                for (int i = 0; i < numCourses; i++) {            if (!dfs(i, prerequisites, visited, stack)) return new int[0];        }                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, int[] visited, Stack<Integer> stack) {        if (visited[i] == 1) return true;        if (visited[i] == -1) return false;                visited[i] = -1;        for (int[] prerequisite : prerequisites) {            if (prerequisite[0] == i) {                if (!dfs(prerequisite[1], prerequisites, visited, stack)) return false;            }        }        visited[i] = 1;        stack.push(i);        return true;    }}


换成邻接表的形式。Time Complexity - O(V + E), Space Complexity - O(V)。

BFS:

public class Solution { // 10ms    public int[] findOrder(int numCourses, int[][] prerequisites) {        if (numCourses <= 0 || prerequisites == null) return new int[0];        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[0];    }}

DFS:

public class Solution {  // 9ms    public int[] findOrder(int numCourses, int[][] prerequisites) {        if (numCourses <= 0) return new int[0];                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][0]).add(prerequisites[i][1]); // 原作者这里代码是反的,那样输出会逆序                int[] visited = new int[numCourses];        Stack<Integer> stack = new Stack<Integer>();                for (int i = 0; i < numCourses; i++) {            if (!dfs(i, graph, visited, stack)) return new int[0];        }                int[] res = new int[numCourses];        for (int i = numCourses - 1; i >= 0; i--) res[i] = stack.pop();        return res;    }        private boolean dfs(int i, List<List<Integer>> graph, int[] visited, Stack<Integer> stack) {        if (visited[i] == 1) return true;        if (visited[i] == -1) return false;                visited[i] = -1;        for (int num : graph.get(i)) {            if (visited[num] == -1) return false;            if (!dfs(num, graph, visited, stack)) return false;        }                visited[i] = 1;        stack.push(i);        return true;    }}

10个代码片除了自己的2个,剩下的本来来自原作者直接copy就完了,结果看着原作者BFS多了个RES不爽,DFS多个stack而且用了俩boolean数组不爽,8个代码片运行了2 30次。。吐血。


0 0
原创粉丝点击