- 题目链接:207. 课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
- 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
1 - 思路
- 题目考察的最终结果是,所用课程是否学完,则所有图的结点是否已经访问到,若可以通过图的结构访问完,证明可以学完所有课程。
- 解法:为有向图建立拓扑排序
- 记录结点的 入度 和 出度
- 入度维护:通过一个
**int**
数组维护结点的入度 - 出度维护:通过
List<List<Integer>> outNode
来维护出图中所有节点的后继结点
2 - 实现
BFS 过程 模拟
构建依赖图
- 初始化outNode和inNode。对于4个课程(编号0到3),outNode初始化为4个空列表,inNode初始化为4个0。
- 根据先决条件更新outNode和inNode:
- 对于[1,0],将1添加到0的后继课程列表中,inNode[1]加1。
- 对于[2,0],将2添加到0的后继课程列表中,inNode[2]加1。
- 对于[3,1],将3添加到1的后继课程列表中,inNode[3]加1。
- 对于[3,2],将3添加到2的后继课程列表中,inNode[3]再加1。
- 此时,outNode和inNode如下:
- outNode = [[1, 2], [3], [3], []]:课程0的后继课程是1和2,课程1和2的后继课程是3,课程3没有后继课
- inNode = [0, 1, 1, 2]:课程0没有先决条件,课程1和2各有一个先决条件,课程3有两个先决条件。
执行BFS
- 将所有入度为0的课程(即没有先决条件的课程)加入队列。这里是课程0。
- 开始BFS过程:
- 从队列中取出课程0,将visited计数加1。
- 查看课程0的后继课程(1和2),将它们的入度减1。此时,inNode = [0, 0, 0, 2]。
- 由于课程1和2的入度变为0,将它们加入队列。
- 接下来,队列中有课程1和2。依次取出这两个课程,对于每个课程,重复上述减少入度并检查后继课程的过程。
- 取出课程1,将visited计数加1,减少课程3的入度(inNode[3]变为1)。
- 取出课程2,将visited计数加1,再次减少课程3的入度(inNode[3]变为0),此时课程3的入度为0,将其加入队列。
- 最后,取出课程3,将visited计数加1。此时,所有课程的入度都为0,且visited计数为4,等于课程总数。
⭐课程表 —— BFS 思路如下
class Solution {
List<List<Integer>> outNode; // 出度集合
int[] inNode; // 入度个数
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 初始化出度集合
outNode = new ArrayList<>();
for(int i = 0 ; i<numCourses;i++){
outNode.add(new ArrayList<>());
}
// 加入出度结点
// 初始化入度数组
inNode = new int[numCourses];
for(int[] num:prerequisites){
outNode.get(num[1]).add(num[0]);
inNode[num[0]]++;
}
Queue<Integer> queue = new LinkedList<>();
// 添加入度为 0 的元素到 queue 代表起始点
for(int i = 0 ; i < inNode.length ;i++){
if(inNode[i]==0){
queue.offer(i);
}
}
int visited = 0;
while(!queue.isEmpty()){
int u = queue.poll();
for(int v : outNode.get(u)){
// 遍历到 v 证明可以学习到v
// 此时 v 入度要减少
inNode[v]--;
if(inNode[v]==0){
queue.offer(v);
}
}
visited++;
}
return visited == numCourses;
}
}
3 - ACM模式
public class hot52_canFinish {
static List<List<Integer>> outNode; // 出度集合
static int[] inNode; // 入度个数
public static boolean canFinish(int numCourses, int[][] prerequisites) {
// 初始化出度集合
outNode = new ArrayList<>();
for(int i = 0 ; i<numCourses;i++){
outNode.add(new ArrayList<>());
}
// 加入出度结点
// 初始化入度数组
inNode = new int[numCourses];
for(int[] num:prerequisites){
outNode.get(num[1]).add(num[0]);
inNode[num[0]]++;
}
Queue<Integer> queue = new LinkedList<>();
// 添加入度为 0 的元素到 queue 代表起始点
for(int i = 0 ; i < inNode.length ;i++){
if(inNode[i]==0){
queue.offer(i);
}
}
int visited = 0;
while(!queue.isEmpty()){
int u = queue.poll();
for(int v : outNode.get(u)){
// 遍历到 v 证明可以学习到v
// 此时 v 入度要减少
inNode[v]--;
if(inNode[v]==0){
queue.offer(v);
}
}
visited++;
}
return visited == numCourses;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入课程数");
int numcourses = sc.nextInt();
System.out.println("输入二维数组 行 row");
int row = sc.nextInt();
int col = 2;
System.out.println("输入课程关系");
int[][] grid = new int[row][col];
for(int i = 0; i < row;i++){
for (int j = 0; j < col;j++){
grid[i][j] = sc.nextInt();
}
}
System.out.println("能否完成完课程"+canFinish(numcourses,grid));
}
}