使用一个队列来进行广度优先搜索。初始时,所有入度为 0 的节点都被放入队列中,它们就是可以作为拓扑排序最前面的节点,并且它们之间的相对顺序是无关紧要的。
在广度优先搜索的每一步中,取出队首的节点 u:
- 将 u 放入答案中;
- 移除 u 的所有出边,也就是将 u 的所有相邻节点的入度减少 1。如果某个相邻节点 v 的入度变为 0,那么就将 v 放入队列中。
在广度优先搜索的过程结束后。如果答案中包含了这 n 个节点,那么就找到了一种拓扑排序,否则说明图中存在环,也就不存在拓扑排序了。
/**
* 课程表 II,拓扑排序,宽度优先
* @param numCourses 课程数
* @param prerequisites
* @return
*/
public static int[] findOrder(int numCourses, int[][] prerequisites){
int[][] edges = new int[numCourses][numCourses]; // 邻接矩阵存储有向图
int[] inDeg = new int[numCourses]; // 各顶点入度
int[] result = new int[numCourses]; // 结果
int index = 0;
for (int i = 0; i < prerequisites.length; i++) { // 初始化
int a1 = prerequisites[i][0], a2 = prerequisites[i][1];
edges[a2][a1] = 1;
inDeg[a1]++;
}
Deque<Integer> queue = new LinkedList<>(); //队列
for (int i = 0; i < numCourses; i++) {
if (inDeg[i] == 0){
queue.add(i); //入度为0的入队
}
}
while (!queue.isEmpty()){
int u = queue.poll();
result[index++] = u;
for (int i = 0; i < numCourses; i++) {
if(edges[u][i] == 1){
inDeg[i]--; // 以u为起始的,入度减一
if(inDeg[i] == 0)
queue.add(i);
}
}
}
if(index != numCourses){ // 有环,无解
return new int[0];
}
return result; //无环,返回结果,结果不唯一
}