判断无向图是否有环路的方法 -并查集 -BFS

Posted 我们都曾拥有最美的时光

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了判断无向图是否有环路的方法 -并查集 -BFS相关的知识,希望对你有一定的参考价值。

可以利用并查集或者带颜色标记的BFS(来自算法导论)判断。

首先介绍第一种,用并查集来判断:

首先初始化所有元素的根为-1,-1代表根节点,接下来对于图中的每一条边(v1,v2)都并入集合,并入的方式为查找v1和v2的根节点,然后让v2的根节点作为v1的根节点,查找根节点的过程为:如果当前的结点根为-1,说明这个结点就是根,直接返回,否则再继续查找结点父亲的根,直到找到祖先结点,这里因为只是判断环路,不需要压缩路径:

int findSet(int x)

    if(Parent[x] == -1)
        return x; // x is root
    
    return findSet(Parent[x]);


在找到v1的根vp1和v2的根vp2以后,首先判断他们是否是同根的,对于一个无环图,某条边并入集合前是不会出现同根的情况的,这是因为这条边中一定有一个结点是新加入集合的(否则这条边就重复了),这个结点的根一定为-1,而另一个已经并入的,会存着根结点的序号(不一定是祖先,因为没有压缩路径),只有图有环的时候才可能两个根相同,因此以vp1是否等于vp2作为图是否有环的依据,一旦发现,即说明有环,直接返回,没有则合并v1、v2,继续进行。

注意:虽然是无向图,但是边只能单向遍历,如果把两个方向的边都遍历,势必有一边出现同根的两结点,一定要注意!!!

具体代码如下:

#include <iostream>
#include <vector>
#include <memory.h>

using namespace std;

vector<int> Parent;

void initSet()

    for(int i = 0; i < Parent.size(); i++)
        Parent[i] = -1;



int findSet(int x)

    if(Parent[x] == -1)
        return x; // x is root
    
    return findSet(Parent[x]);



void UnionSet(int x, int y)

    int xp = findSet(x);
    int yp = findSet(y);
    Parent[xp] = yp;



int main()

    int N, E;
    cin >> N >> E;
    vector<vector<int> > edges(N);
    Parent.resize(N);
    int v1,v2;
    for(int i = 0; i < E; i++)
        cin >> v1 >> v2;
        edges[v1].push_back(v2);
        //edges[v2].push_back(v1);
    
    // 测试是否有环
    initSet();
    for(int v = 0; v < edges.size(); v++)
        for(int i = 0; i < edges[v].size(); i++)
            int w = edges[v][i];
            int xp = findSet(v);
            int yp = findSet(w);
            if(xp == yp)
                cout << "未合并前同根,说明有环。" << endl;
                return 0;
            
            UnionSet(v,w);
        
    
    cout << "无环" << endl;
    return 0;


第二种方法,是利用BFS。

我们规定结点有三种颜色,白色、灰色、黑色,在结点没有访问之前,为白色,当结点入队时,结点变灰,出队时变黑。

因为BFS是按层的顺序、从左到右进行遍历的,因此当一个根结点变黑后,也就是它出队以后,接下来要将它的所有未访问过的子结点(邻接点)入队,并且染上灰色,下面我们讨论任一个子结点的颜色。

如果没有环,子结点的颜色只可能是白色,也就是未访问过,如果子结点的颜色为灰色,说明入队过,可能是在根结点变黑(出队)之前就有一个结点有这个子结点作为邻接点,从而进行了第一次访问,这也就是有环的情况,因此,只需要在BFS过程中检测出队结点的邻接点是否有灰色结点即可,有灰色结点可理解得出有环的结论。

具体代码如下:

bool hasCycle(int s)

    for(int i = 1; i <= N; i++) 
        nodesColor[i] = colorWhite;
    

    queue<int> Q;
    Q.push(s);
    while(!Q.empty())

        int v = Q.front();
        Q.pop();
        nodesColor[v] = colorGray;
        for(int index = 0; index < Graph[v].size(); index++)
            int w = Graph[v][index];
            if(nodesColor[w] == colorWhite)
                Q.push(w);
                nodesColor[w] = colorGray;
            else if(nodesColor[w] == colorGray)
                    return true;
            
        
        nodesColor[v] = colorBlack;
    

    return false;




以上是关于判断无向图是否有环路的方法 -并查集 -BFS的主要内容,如果未能解决你的问题,请参考以下文章

如何判断一个图是不是为有向无环图(DAG)

欧拉路径和欧拉回路判断方法

判断无向图/有向图中是否存在环

如何判断一个图中是否存在环路

求无向图中的所有简单环路

HDU-1878 判断无向图欧拉回路,水