您的位置:首页 > 其它

207. Course Schedule

2015-05-11 03:53 429 查看
题目:

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:
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.

Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort.

Topological sort could also be done via BFS.

链接: http://leetcode.com/problems/course-schedule/

题解:

求Course Schedule,等同问题是有向图检测环,vertex是course, edge是prerequisite。我觉得一般会使用Topological Sorting拓扑排序来检测。一个有向图假如有环则不存在Topological Order。一个DAG的Topological Order可以有大于1种。 常用的Topological Sorting算法有两种

Kahn's Algorithms (wiki): BFS based, start from with vertices with 0 incoming edge,insert them into list S,at the same time we remove all their outgoing edges,after that find new vertices with 0 incoming edges and go on. 详细过程见Reference里Brown大学的课件。

Tarjan's Algorithms (wiki): DFS based, loop through each node of the graph in an arbitrary order,initiating a depth-first search that terminates when it hits any node that has already been visited since the beginning of the topological sort or the node has no outgoing edges (i.e. a leaf node). 详细过程见Reference里 NYU的课件。

知道了理论,下面就是coding了,早就发现自己的coding能力确实比较弱,即使明白算法也很难迅速写出正确的程序。多练习,一点点进步吧。互换edge[0]和edge[1]也能检测环,不过输出的就是逆序的Tolopolical order了。

Kahn's Algorithms:

Time Complexity - O(VE), Space Complexity - O(V)。 这里需要再研究一下怎么做到O(V + E)。

public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) { // Kahn's Algorithm
if(numCourses <= 0)
return false;
if(prerequisites == null || prerequisites.length == 0)
return true;
int[] inDegree = new int[numCourses];

for(int[] edge : prerequisites)
inDegree[edge[0]]++;

List<Integer> res = new ArrayList<>();
Queue<Integer> queue = new LinkedList<>();

for(int i = 0; i < inDegree.length; i++) {
if(inDegree[i] == 0)
queue.offer(i);
}

while(!queue.isEmpty()) {
int source = queue.poll();
res.add(source);

for(int[] edge : prerequisites) {
if(edge[1] == source) {
inDegree[edge[0]]--;
if(inDegree[edge[0]] == 0)
queue.offer(edge[0]);
}
}
}

return res.size() == numCourses;
}
}


Tarjan's Algorithms:

DFS based。以任意顺序遍历图的Vertices,假如在一次dfs中,在一条从source vertex到target vertex的edge中,发现target vertex已经在这次dfs的onStack中被mark了,那么我们设置result = false,返回。结束便利这个source vertex所有的edge之后,push这个vertex to stack,这个stack是reversePost顺序的。所以最后输出时,从栈顶一个一个pop元素组成的list就是一种topological order。

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 boolean canFinish(int numCourses, int[][] prerequisites) {
if(prerequisites == null || prerequisites.length < 2)
return true;
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 false;
if(!marked[v])
dfs(v, prerequisites);
}

return true;
}

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
}
}


二刷:

想要快只有一个办法,就是把输入图List of Edges的表达方式转变为Adjacency Lists的表达方式,留给下一次刷了。 对图的表达形式还需要再复习。

这回还是用的老方法。

Java:

Kahn's Method - BFS

Time Complexity - O(VE), Space Complexity - O(V)

public class Solution {
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];
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);
}

List<Integer> res = new ArrayList<>();
while (!q.isEmpty()) {
int num = q.poll();
res.add(num);
for (int[] prerequisite : prerequisites) {
if (prerequisite[1] == num) {
inDegree[prerequisite[0]]--;
if (inDegree[prerequisite[0]] == 0) {
q.offer(prerequisite[0]);
}
}
}
}
return res.size() == numCourses;
}
}


Tarjan's method - DFS

好慢的速度...这里其实可以不用加stack操作。加上的话,最后把stack内元素全部pop出来加入到一个list里也就是一个topological order。

Time Complexity - O(VE), Space Complexity - O(V)

public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 0 || prerequisites == null) return false;
if (prerequisites.length == 0) return true;
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 false;
}
return true;
}

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 (!visited[prerequisite[1]]) {
if (!dfs(prerequisite[1], prerequisites, visited, onVisiting, stack)) return false;
}
}
}
onVisiting[i] = false;
stack.push(i);
return true;
}
}


没忍住还是来做优化了...换了表示方式以后速度提高了近10倍 -___-!

Kahn's Method - BFS using Graph as Adjacency Lists

Time Complexity - O(V + E), Space Complexity - O(V)

public class Solution {
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);
}

List<Integer> res = new ArrayList<>();
while (!q.isEmpty()) {
int num = q.poll();
res.add(num);
for (int i : graph.get(num)) {
inDegree[i]--;
if (inDegree[i] == 0) {
q.offer(i);
}
}
}
return res.size() == numCourses;
}
}


Tarjan's Method - BFS using Graph as Adjacency Lists

Time Complexity - O(V + E), Space Complexity - O(V)

public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 0 || prerequisites == null) return false;
if (prerequisites.length == 0) return true;

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 false;
}
return true;
}

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;
}
}


Reference:
https://en.wikipedia.org/wiki/Cycle_detection https://en.wikipedia.org/wiki/Topological_sorting http://cs.brown.edu/courses/cs016/lectures/14%20DAGS%20and%20Top%20Sort.pdf http://www.cs.nyu.edu/courses/summer04/G22.1170-001/6a-Graphs-More.pdf https://leetcode.com/discuss/76205/6ms-java-dfs-solution http://www.geeksforgeeks.org/detect-cycle-undirected-graph/ http://www.geeksforgeeks.org/detect-cycle-in-a-graph/ http://stackoverflow.com/questions/261573/best-algorithm-for-detecting-cycles-in-a-directed-graph https://leetcode.com/discuss/34791/bfs-topological-sort-and-dfs-finding-cycle-by-c https://leetcode.com/discuss/39456/java-dfs-and-bfs-solution https://leetcode.com/discuss/42543/18-22-lines-c-bfs-dfs-solutions https://leetcode.com/discuss/35035/oo-easy-to-read-java-solution https://leetcode.com/discuss/35578/easy-bfs-topological-sort-java http://rosettacode.org/wiki/Topological_sort http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html https://leetcode.com/discuss/69387/34ms-java-bfs-toposort-with-kahns-algorithm https://leetcode.com/discuss/69396/20ms-java-dfs-toposort-with-tarjans-algorithm http://www.1point3acres.com/bbs/thread-136257-1-1.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: