题目描述
现在你总共有 numCourses
门课需要选,记为 0
到 numCourses - 1
。给你一个数组 prerequisites
,其中 prerequisites[i] = [ai, bi]
,表示在选修课程 ai
前 必须 先选修 bi
。
例如,想要学习课程 0
,你需要先完成课程 1
,我们用一个匹配来表示:[0,1]
。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。
算法描述
使用广度优先搜索的策略,先将所有入度为 0 的课程加入队列,表明没有前置课程未修。
遍历队列中的元素,将队列的元素加入结果数组,同时,将该可修课程的后继课程的入度减 1,判断后继课程此时的入度是否为 0。若入度为 0,表明所有前继课程均已修完,可以加入队列。
不断进行上述操作,直到队列为空。若所有课程均加入结果数组,则说明存在这样的一个拓扑序列;否则不可能完成所有课程,返回一个空数组。
复杂度分析
时间复杂度: O ( n + m ) O(n + m) O(n+m),其中 n 为课程数,m 为先修课程的要求数。本质上就是对图进行广度优先搜索的时间复杂度。
程序代码
class Solution {
public:
vector<int> topsort(vector<int>& d, const vector<vector<int>>& g) {
int n = d.size();
queue<int> q;
vector<int> res;
for(int i = 0; i < n; i++) {
// 入度为0入队
if( !d[i] ) {
q.push(i);
}
}
while( !q.empty() ) {
int t = q.front();
q.pop();
res.push_back(t);
for(auto it : g[t]) {
if( --d[it] == 0 ) {
q.push(it);
}
}
}
if(res.size() == n) return res;
else return vector<int>();
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> g(numCourses, vector<int>());
// 记录每个节点的入度
vector<int> d(numCourses, 0);
for(auto p : prerequisites) {
int a = p[0], b = p[1];
g[b].push_back(a);
d[a]++;
}
return topsort(d, g);
}
};