题目:

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. Git初始化与上传

    一: 现在git上Create个repository 二:进入要长传的工程目录打开git bash git initgit statusgit add .//add .的时候文件不要被占用. git ...

  2. DP入门数塔问题

    在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?        已经告诉你了,这 ...

  3. ssh 私匙登录, 文件rswrst权限

    skill -KILL -u user1  //注销用户 ssh 免密码登录 http://flysnowxf.iteye.com/blog/1567570 (说是防火墙的问题) http://fly ...

  4. CCNA第一讲笔记

    园区网:一组连续的局域网(校园网.企业内部网) 园区网拓扑: 一层楼的PC连接到一台交换机(同一层的PC可以互联):一栋楼的每层的交换机连接到同一台交换机(整栋楼的PC可以互联):每栋楼的交换机连接到 ...

  5. c#怎么获取当前页面的url

    Request.ApplicationPath: /testwebRequest.CurrentExecutionFilePath: /testweb/default.aspxRequest.File ...

  6. 01_mvc保存时出错

    修改实体类报错 存储区更新.插入或删除语句影响到了意外的行数(0).实体在加载后可能被修改或删除.刷新 ObjectStateManager 项.   原因是 数据表中的自增主键列未赋值.

  7. js中使用使用原型(prototype)定义方法的好处

    经常在前端面试或是和其他同行沟通是,在谈到构造在JS定义构造函数的方法是最好使用原型的方式:将方法定义到构造方法的prototype上,这样的好处是,通过该构造函数生成的实例所拥有的方法都是指向一个函 ...

  8. HTML中href的链接刷新页面问题

    在上一篇随笔中说到了html()方法不能一直改变标签的值的问题,当单击完成时,回调函数返回的值瞬间就没有了,今天突然想到了,我单击的是链接啊,就算链接到本界面上,也要进行刷新,页面一刷新,显示的值自然 ...

  9. c语言文件操作函数详解

    一.文件操作注意点: 1 打开文件时,如果打开方式加“+”,表示该文件可以“写” ; 2 退出程序一般用exit函数,正常退出参数为0,非正常退出参数为正零值 ; 3 文件的读写操作:按字符.字符串. ...

  10. CentOS 下 Codeblocks 的 安装 + 汉化 以及 基本使用介绍

    Codeblocks 安装 注:在root用户下运行下列命令 1.安装gcc,需要c和c++两部分,默认安装下,CentOS不安装编译器的,在终端输入以下命令即可 yum install gcc yu ...