题意:给出有向图,判断是否有环
分析与思路:题目看上去简单,但是没有一个套路还是挺难想的,求有向图是否有环,有一个思路清晰又实用的方法,就是用dfs遍历同时判断是否有回边,pre[node]和post[node]分别代表访问这个点的开始时间和离开时间,一个边u->v若满足pre(v)<pre(u)<post(u)<post(v)则为回边(这是一个定理,怎么证明具体可以百度),有一个小问题就是给出的图不一定是连通的,可以找出所有入度为0的节点保存起来作为源集合,对于每个源都来dfs一遍就可以。具体有个问题就是当你访问节点u时是还不知道post(u)或者可能还不知道post(v)的值的。这个可以隐形比较,当深搜到点u时,对于u指向的所有节点v,若pre[v]有值则必有pre[v]<pre[u],同样道理,若post[v]没有设置过,则最后必有post[u]<post[v],所以判断条件可以转化为判断是否存在指向的节点满足pre[v]已设置过值同时post[v]没设置过值的,则u->v为回边
代码:
class Solution {
public:
vector<bool> isVisited;//可以用pre代替的
vector<int> pre;
vector<int> post;
int clock;
bool dfs(int nowNode, vector<pair<int, int>>& prerequisites) {
isVisited[nowNode] = true;
pre[nowNode] = clock++;
for (int i = 0; i < prerequisites.size(); i++) {
if (prerequisites[i].first == nowNode) {
if (isVisited[prerequisites[i].second] == true && post[prerequisites[i].second] == -1) return false;//有回边
else if (isVisited[prerequisites[i].second] == false) {
if (!dfs(prerequisites[i].second, prerequisites)) return false;
}
}
}
post[nowNode] = clock++;
return true;
}
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
if (prerequisites.size() == 1) return true;
isVisited=vector<bool>(numCourses, false);//标记是否访问过
pre=vector<int>(numCourses, -1);
post = vector<int>(numCourses, -1);
clock = 0;
vector<int> toVisited;//源集合
bool flag = true;
vector<bool> hasIn(numCourses, false);//判断找出源
for (int i = 0; i < prerequisites.size(); i++) {
hasIn[prerequisites[i].second] = true;
}
for (int i = 0; i < hasIn.size(); i++) {
if (hasIn[i] == false) toVisited.push_back(i);
}
if (toVisited.empty()) flag = false;//没有源
if (flag != false) {
for (int i = 0; i < toVisited.size(); i++) {
if (!dfs(toVisited[i], prerequisites)) {
flag = false;
break;
}
}
}
for (int i = 0; i < numCourses; i++) {//是否有把所有点遍历过
if (!isVisited[i]) return false;
}
return flag;
}
};