207. Course Schedule
拓扑排序 Topological Order
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。
通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。
简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
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.
分析
- 通过计算所有点的inDegree(入度),找到拓扑序列中可能是开头的点(inDegree[node]==0)。将这些点加入队列queue。
- 遍历队列,对每一个队列中的点cur_node:
找到他的一个post_node,并将inDegree[post_node]- –
如果inDegree[post_node]==0 ,把post_node也加入队列。 - 遍历所有inDegree,如果inDegree[node]!=0。说明图中有环,说明有node没有被加入queue。
(那只可能是因为这个node在他的pre_node还没被访问到的时候,就被访问了。)
Code
public class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 0) return false;
Queue<Integer> queue = new LinkedList<Integer>(); //LinkedList实现了Queue接口
int inDegree[] = new int[numCourses];
for(int i=0; i<prerequisites.length; i++){
inDegree[prerequisites[i][1]]++;
}
for(int i=0; i<numCourses; i++){
if(inDegree[i]==0){// this node don't have prenode
queue.offer(i);
}
}
Integer curNode;
while((curNode = (Integer)queue.poll())!=null){
for(int i=0; i<prerequisites.length; i++){
if(prerequisites[i][0]==curNode){
inDegree[prerequisites[i][1]]--;
if(inDegree[prerequisites[i][1]]==0){
queue.offer(prerequisites[i][1]);
}
}
}
}
for(int i=0; i<numCourses; i++){
if(inDegree[i]!=0) return false;
}
return true;
}
}
java Queue中 remove/poll, add/offer, element/peek区别
offer,add区别:
一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝。
这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer() 返回的 false。
poll,remove区别:
remove() 和 poll() 方法都是从队列中删除第一个元素。remove() 的行为与 Collection 接口的版本相似,
但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。
peek,element区别:
element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null