题目来源
题目描述
题目解析
本题可以分解为两个问题:
- 能否修完所有课程:必须不形成环
- 最少需要多少学期才能学完全部课程:就是要求拓扑排序的层数
我们可以将课程看成是有向图,课程A指向课程B,则B的入度加一
入度作为课程能否在本学期内开课的根据
如果一个课程的入度为0,说明现在没有该课程的先修课程,可以直接上了。否则不能直接上,必须等到先修课程上完之后才能上它。
逻辑:
1、针对给定的课程关系,统计课程的入度和课程的所有后继课程;
2、初始时,入度就为 0 的课程就可以在第一学期上;
3、每上完一学期,就把这学期上的课的后继课程的入度减一,然后统计减一后新的入度为 0 的课程,作为下学期的开课课程。
4、经过 3 的循环,当最后一个学期结束,应该所有的课程入度都不大于 0,如果还存在某个课程入度大于 0,说明存在环,返回 -1。否则直接返回 3 的循环次数。
拓扑排序
class Solution {
public:
int minimumSemesters(int N, vector<vector<int>>& relations){
std::vector<int> inDegree(N + 1, 0); // 需要统计每个节点的入度
std::map<int, std::set<int>> map; //需要统计每个节点的后继课程
for(auto iter : relations){
map[iter[0]].insert(iter[1]);
inDegree[iter[1]]++;
}
// 先将入度为0的节点加入std::queue
std::queue<int> queue;
for (int i = 1; i < inDegree.size(); ++i) { // 注意: dp[0]是无效的
if(inDegree[i] == 0){
queue.push(i);
}
}
int cnt = 0;
while (!queue.empty()){
cnt++; // 新学期开始了
int size = queue.size(); // 将当前入度为0的课程都修完, 本次课程需要修size门课程
for (int i = 0; i < size; ++i) {
int curr = queue.front(); queue.pop(); // 将当前课的所有后置课程的入度减少1
for(auto next : map[curr]){
--inDegree[next];
if(inDegree[next] == 0){
queue.push(next);
}
}
}
}
// 是否会形成环
for (int i = 1; i < inDegree.size(); ++i) {
if(inDegree[i] != 0){
return -1;
}
}
return cnt;
}
};