题目:

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. Win7设置共享文件夹

    在系统中,实现共享的前提是,双方要在同一个局域网内,同一个工作组或者域内. 添加共享用户 点击确定即可,最好把共享的权限设置为只读类型,方便管理. 可以使用计算机的用户名或者IP地址来访问 如果还是不 ...

  2. 【转】Qt使用自带的windeployqt 生成exe来发布软件

    集成开发环境 QtCreator 目前生成图形界面程序 exe 大致可以分为两类:Qt Widgets Application  和 Qt Quick Application.下面分别介绍这两类exe ...

  3. CSS3中的变形处理(transform)属性

    在CSS3中,可以利用transform功能来实现文字或图像的旋转.扭曲.缩放.位移.矩阵.原点这六种类型的变形处理,下面将详细讲解transform的使用. 变形--旋转 rotate() div. ...

  4. Cassandra1.2文档学习(16)—— 模式的变化

    参考文档:http://www.datastax.com/documentation/cassandra/1.2/webhelp/index.html#cassandra/dml/dml_schema ...

  5. 深入理解jsavascript的作用域

    一. JavaScript声明提前 在JavaScript中如果不创建变量,直接去使用,则报错: console.log(xxoo); // 报错:Uncaught ReferenceError: x ...

  6. Python学习_算数运算函数

    记录以grades列表为例,分别定义输出.求和.平均值.方差和标准差函数,并输出相应的值 grades = [100, 100, 90, 40, 80, 100, 85, 70, 90, 65, 90 ...

  7. 1. Linux驱动开发之开篇--Makefile

    基本Makefile假设现在有3个文件,file2.h是函数声明,file2.c是函数定义,文件file1.c调用file2.c中的函数.则Makefile文件的编写如下: helloworld:fi ...

  8. N皇后摆放问题

    Description 在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上.  你的任务是,对于给定的N,求出有多少种 ...

  9. cmd&Linux 下使用mysql全记录

    php mysql数据库常用cmd命令集 show databases; 显示数据库 create database name; 创建数据库 use databasename; 选择数据库 drop ...

  10. SQL学习中(一)序列

    序列可以理解数值序列生成器,通俗的说是按照已经设定的规则自动产生数据的方案对象.--SQL SERVER不支持 个人认为序列类似于SQLSERVER中的identity(1,1),可以用于在表中添加数 ...