题目:

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

210. Course Schedule II的更多相关文章

  1. 【LeetCode】210. Course Schedule II

    Course Schedule II There are a total of n courses you have to take, labeled from 0 to n - 1. Some co ...

  2. 【刷题-LeetCode】210. Course Schedule II

    Course Schedule II There are a total of n courses you have to take, labeled from 0 to n-1. Some cour ...

  3. [LeetCode] 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 prereq ...

  4. Java for LeetCode 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 prer ...

  5. Leetcode 210 Course Schedule II

    here are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have prere ...

  6. LeetCode 210. Course Schedule II(拓扑排序-求有向图中是否存在环)

    和LeetCode 207. Course Schedule(拓扑排序-求有向图中是否存在环)类似. 注意到.在for (auto p: prerequistites)中特判了输入中可能出现的平行边或 ...

  7. [LeetCode] 210. Course Schedule II 课程安排II

    There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have prer ...

  8. 【LeetCode】210. Course Schedule II 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 拓扑排序,BFS 拓扑排序,DFS 参考资料 日期 ...

  9. (medium)LeetCode 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 prer ...

随机推荐

  1. spring @Resource和@Autowired的区别

    @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了.@Resource有两个属性是比较重要的,分 ...

  2. AE实现投影定义和投影转换

    添加引用ESRI.ArcGIS.DataManagementTools 1.获取要定义和要转换的投影 IWorkspaceFactory wsf = new ShapefileWorkspaceFac ...

  3. ie支持CSS3标签

    让IE6/IE7/IE8浏览器支持CSS3属性 by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/w ...

  4. asp:HyperLink vs asp:LinkButton

    asp:HyperLink NavigateUrl - 页面刷新为新的页面 pageload ispostback = false asp:LinkButton PostbackUrl - postb ...

  5. android studio笔记之编译运行错误

    错误类型: Error:java.lang.UnsupportedClassVersionError: com/android/dx/command/Main : Unsupported major错 ...

  6. java开发命名规范总结

    一 包名的书写规范 (Package)推荐使用公司或机构的顶级域名为包名的前缀,目的是保证各公司/机构内所使用的包名的唯一性.包名全部为小写字母,且具有实际的区分意义. 1.1 一般要求1.选择有意义 ...

  7. 格式化说明符定义、转义字符、枚举、结构体、typedef

    1.格式化说明符定义: %i,%d:输出十进制整型数 %6d:输出十进制整型数,至少6个字符宽 %li,%ld:输出长整数 %u:输出无符号整数 %lu:输出无符号长整数(相当于:unsigned l ...

  8. IOS调用相机相册

    #import "SendViewController.h"  //只能打开,没有加载图片的代码,老代码,供参考 #import <MobileCoreServices/UT ...

  9. ExtJs 4.2.1 复选框数据项动态加载(更新一下)

    最近在做博客项目,后台管理用的是ExtJs4.2.1版本,因为是初学所以在使用的时候也遇到不少的这样或那样的问题,也写了不少这方面的博客,今天要写的博客是关于复选框数据项动态的加载功能,以前也没用过, ...

  10. 1024: [SCOI2009]生日快乐 - BZOJ

    Description windy的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和 Y 的矩形蛋糕.现在包括windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋 ...