在图论中,如果一个有向图从任意顶点出发无法经过若干条边回到该点,则这个图是一个有向无环图(Directed Acyclic Graph,DAG). 对于一个n个节点的有向图(节点编号从0到n-1),请判断其是否为有向无环图.
图的节点数和边数均不多于100000.
请为下面的Solution类实现解决上述问题的isDAG函数,函数参数中n为图的节点数,edges是边集,edges[i]表示第i条边从edges[i].first指向edge[i].second. 如果是有向无环图返回true,否则返回false.
class Solution {
public:
bool isDAG(int n, vector<pair<int, int>>& edges) {
}
};
例1:
n = 3,edges = {(0, 1), (0, 2)},函数应返回true.
例2:
n = 2,edges = {(0, 1), (1, 0)},函数应返回false.
主要思想:拓扑排序,先把边集转化为邻接表,同时计算各个点的入度。维持一个栈来保存已经排好序的点。一开始先把图中所有入度为0的点先入栈,然后开始循环。取出栈顶元素,想象在图中删除它,然后删除以它为源点的边,同时维持别的点的入度。如果出现入度为0的点,入栈。最后栈为空时,退出循环。复杂度O(V+E)
// Problem#: 20620
// Submission#: 5138131
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
class Solution {
public:
bool isDAG(int n, vector<pair<int, int> >& edges) {
vector<vector<int> > matrix(n);
int degree[100000];
for(int i = 0; i < n; i++)degree[i] = 0;
for(int i = 0; i < edges.size(); i++){
matrix[edges[i].first].push_back(edges[i].second);
degree[edges[i].second]++;
}
int count = 0;
stack<int> q;
for(int i = 0; i < n; i++)
if(degree[i] == 0){
count++;
q.push(i);
}
while(!q.empty()){
int j = q.top();
q.pop();
for(int i = 0; i < matrix[j].size(); i++){
degree[matrix[j][i]]--;
if(degree[matrix[j][i]] == 0){
q.push(matrix[j][i]);
count++;
}
}
}
return count == n? true: false;
}
};
然后是DFS的做法,一开始的想法是取一个点做DFS,如果遍历到的点已经在路径上,则有环。但如果图存在不联通的两个孤岛,DFS就会中断。这时还需要加一层循环,这样会超时(我写的会超时)。
下面用递归的办法做DFS,给所有顶点都判断一遍。函数check的作用是判断顶点v开始的DFS是否有回路,若有,返回true。这个略快一点。
class Solution {
public:
int visit[100000];
bool isDAG(int n, vector<pair<int, int> >& edges) {
vector<vector<int> > matrix(n);
for(int i = 0; i < edges.size(); i++){
matrix[edges[i].first].push_back(edges[i].second);
}
for(int i = 0; i < n; i++)visit[i] = 0;
for(int i = 0; i < n; i++)
if(check(i, matrix))return false;
return true;
}
bool check(int v, vector<vector<int> >& matrix)
{
if(visit[v])return true;
visit[v] = true;
for(int i = 0; i < matrix[v].size(); i++){
if(check(matrix[v][i], matrix))return true;
}
visit[v] = false;
return false;
}
};
这是不用递归版本的DFS证明是不是有环图的方法:
class Solution {
public:
bool isDAG(int n, vector<pair<int, int> >& edges) {
//转换邻接矩阵
vector<vector<int> > matrix(n);
for(int i = 0; i < edges.size(); i++){
matrix[edges[i].first].push_back(edges[i].second);
}
stack<int> q;
int visit[100000];
for(int i = 0; i < n; i++){
visit[i] = -1;
}
for(int i = 0; i < n; i++){//防止出现不连通图的一层循环
q.push(i);
visit[i] = 1;
while(!q.empty()){
int d = q.top(), i;
for(i = 0; i < matrix[d].size(); i++){
if(visit[matrix[d][i]] == -1){
q.push(matrix[d][i]);
visit[matrix[d][i]] = 1;
break;
}
if(visit[matrix[d][i]] == 1)return false;
}
if(i == matrix[d].size()){
q.pop(); visit[d] = 0;
}
}
}
return true;
}
};