题面:
你这个学期必须选修 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 。这是可能的。
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
提示:
1 <= numCourses <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i]
中的所有课程对 互不相同
是一道很明显的拓扑排序题,但可以使用邻接表来存储所有的边,非常的节省空间
邻接表插入的代码:
int idx=0;
int e[M],ne[M],h[N];//如果边有长度加一个we[M]即可
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
当需要变量一个节点的所有边时:
for(int i=h[a];i!=-1;i=ne[i]){//a是左节点
b=e[i];//此时的b就是右节点
}
拓扑排序就比较简单了:只需要找入度为零的点输入到队列中,然后遍历它为左节点的所有边,删除(右节点的入度–),最后如果所有节点的入度都为0,那么就可以了(拓扑排序不唯一)
该题目我的代码:
class Solution {
public:
int h[2005],ne[5005],e[5005],idx=0;
int v[2005];
bool vis[2005];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
// 学一下邻接表,用邻接表存储更方便
// 拓扑排序经典题
// 使用队列来存储
queue<int> q;
int n=prerequisites.size();
memset(h,-1,sizeof h);
for(int i=0;i<n;i++){
add(prerequisites[i][0],prerequisites[i][1]);
v[prerequisites[i][1]]++;
// vis[]
}
// int len = v.size();
for(int i=0;i<2005;i++){
if(v[i]==0) q.push(i);
}
while(q.size()){
int t = q.front();
cout<<t<<endl;
q.pop();
vis[t]=true;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
v[j]--;
if(v[j]==0) q.push(j);
}
}
for(int i=0;i<2005;i++){
if(v[i]) return false;
}
return true;
}
};
喜欢的话请一件三连,人人都能收获满满offer