二种方法:深度优先dfs、拓扑排序
dfs
对一个起始顶点进行dfs遍历,如果访问到了已经访问过的顶点,则有环,但是这个重复访问的顶点上一次访问必须是当前起始顶点发起的dfs访问的,如果是其他起始顶点发起的dfs遍历过这个点,则不是环;也就是说只有在同一次dfs遍历中重复访问到了某个节点才算是有环; 使用一个visited数组记录节点状态,0标识未被访问过,1标识在当前这次dfs遍历中被访问过,-1标识在当前dfs遍历中未被访问过,但是在其他dfs遍历中被访问过:
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> mmap(numCourses, vector<int>());
vector<int> visited(numCourses, 0);
for(int i=0; i<prerequisites.size(); i++)
mmap[prerequisites[i][1]].push_back(prerequisites[i][0]);
for(int i=0; i<numCourses; i++)
if(!dfs(mmap, visited, i)) return false;
return true;
}
bool dfs(vector<vector<int>>& mmap, vector<int>& visited, int k){
if(visited[k] == 1) return false;
if(visited[k] == -1) return true;
visited[k] = 1;
for(int i=0; i<mmap[k].size(); i++){
if(!dfs(mmap, visited, mmap[k][i]))
return false;
}
visited[k] = -1;
return true;
}
}
拓扑排序(广度优先)
核心思想是不断找没有前驱只有后继的节点压入栈或者队列,然后删去图中以它为前驱的边,输出这个节点(也就是计数器加1),直到没有这样的节点结束,此时若计数器和图中顶点个数相同,则无环,否则有:
bool tp(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> mmap(numCourses, vector<int>(numCourses, 0));
vector<int> indegree(numCourses, 0);
for(int i=0; i<prerequisites.size(); i++){
mmap[prerequisites[i][0]][prerequisites[i][1]] = 1;
indegree[prerequisites[i][0]] += 1;
}
stack<int> mstack;
int res=0;
for(int i=0; i<numCourses; i++)
if(!indegree[i]) mstack.push(i);
while(!mstack.empty()){
int k = mstack.top();
mstack.pop();
res++;
for(int i=0; i<numCourses; i++){
if(mmap[i][k]){
mmap[i][k] = 0;
indegree[i]--;
if(!indegree[i]) mstack.push(i);
}
}
}
if(res < numCourses) return false;
else return true;
}