207. 课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
看着就有点像有向图相关的东西,拓扑排序之前数据结构也学过,之前学的就是广度优先遍历,深度的懒得学了。当然还是写不出来,直接看答案。答案连个注释都没有,我添加了一点注释。
构建图的时候最多只有课程数目那么多条边。比如课程数=2,有向图是0–>1–>2–>3–>4.构建出图就是0–>1–>2.这个是存在拓扑排序的。但是visted的节点是3,课程是2就会返回false。所以好像没必要把整个图构建出来?
class Solution {
// 用于构建有向图
List<List<Integer>> edges;
// 用于记录每个节点的入度
int[] indeg;
public boolean canFinish(int numCourses, int[][] prerequisites) {
edges = new ArrayList<List<Integer>>();
for (int i = 0; i < numCourses; i++) {
edges.add(new ArrayList<Integer>());
}
indeg = new int[numCourses];
for (int[] info : prerequisites) {
// 构建有向图
edges.get(info[1]).add(info[0]);
// 记录每个节点入度
++indeg[info[0]];
}
Queue<Integer> queue = new LinkedList<Integer>();
for (int i = 0; i < numCourses; i++) {
if (indeg[i] == 0) {
// 入度为0的节点入队列
queue.offer(i);
}
}
// 拓扑排序结果的节点数目
int visited = 0;
while (!queue.isEmpty()) {
++visited;
// 处理队列中的节点
int u = queue.poll();
// 有向图中节点u指向的节点,u移除后,它指向的节点入度应该-1,减到0了也应该入队列
for (int v : edges.get(u)) {
--indeg[v];
if (indeg[v] == 0) {
queue.offer(v);
}
}
}
// 校验拓扑排序结果节点个数等于选修课程数目
return visited == numCourses;
}
}