题目描述(中等)
现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。
例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:[0,1]
解释:总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
示例 2:
输入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
输出:[0,2,1,3]
解释:总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。
示例 3:
输入:numCourses = 1, prerequisites = []
输出:[0]
提示:
1 <= numCourses <= 2000
0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= ai, bi < numCourses
ai != bi
所有[ai, bi] 互不相同
思路
反向思考,一个先修课程对应多个后修课程,即一条条由先修指向后修的边,后修课程的入度即为它所需的先修课程的数量,当其所有先修课程上完才能上。
首先建立一个课程和他后修课程的图,一个课程对应一个vector,同时统计每个后修课程的入度;
首先将入度为0的课程进队,并进答案,即无先修要求的先上完;
之后对队列中每个课程,更新他的后修课程的入度(减一),如果后修课程入度变为0了,说明先修课上完了可以上这门课了,将其加入队列和答案。
当队列为空退出循环,如果答案的size和课程数量不等,说明图里有环,即有前后矛盾的课程先修要求,不能完成所有课程,返回空数组。
代码
class Solution {
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> ans;
unordered_map<int, vector<int>> hash; //每个课程及他的后续课程
vector<int> inedge(numCourses); //每个课程的入度
for(auto x : prerequisites) {
hash[x[1]].push_back(x[0]);
inedge[x[0]]++;
}
queue<int> q;
for(int i = 0; i < numCourses; i++) {
if(inedge[i] == 0) {
q.push(i);
ans.push_back(i);
}
}
while(!q.empty()) {
int cousenow = q.front();
q.pop();
if(hash[cousenow].empty()) continue;
for(auto cousenext : hash[cousenow]) {
inedge[cousenext]--;
if(inedge[cousenext] == 0) { //前置课程上完
q.push(cousenext);
ans.push_back(cousenext);
}
}
}
vector<int> wrongans;
return ans.size() == numCourses ? ans : wrongans;
}
};