有向图无向图判断有环

东北地区赛有个无向图判自环的题当时没苟出来,回来之后想了想用并查集可以实现,然后仔细查了一波还有哪些方法……

总结一波博客吧QAQ……

资源来源:

https://blog.csdn.net/ouyangruo/article/details/51057409

https://blog.csdn.net/acmdream/article/details/72983715

http://www.xuebuyuan.com/2229565.html

https://blog.csdn.net/lezg_bkbj/article/details/11299335

 

有向图:

法一(最佳):拓扑排序

每次找到入度为0的点,按顺序将其标记,若同时有多个则无所谓其间的先后顺序。

然后将这个点删掉,将以其为起点的边都删掉,即修改那些终点的点的入度即可。

然后,如果最后修改完所有的点的入度都是0,则没有环,否则有环。

想想一个1->2->3->1的有向图中的环,这样会导致1、2、3三个点的入度同时为1,找不到入度为0的点。

 

法二:

//似乎2600是邻接矩阵的极限了

//注意到,对于每个点i,i搜索完才会vis[i]=1,在此之前vis[i]=-1

//因此,vis[i]=-1的话,如果有环就会搜到自己从而判YES,如果搜到树的叶子结点,就会把树根改为vis[i]=1,继续搜别的

#include<cstdio>  
#include<cstring>  
int n,c,a[2600][2600],vis[2600];  
int dfs(int v){      
    vis[v] = -1;      
    for(int i = 1; i <= n; i++)      
    {          
        if(a[v][i] != 0 && !vis[i])  {            
            dfs(i);          
            vis[i]=1;  
        }  
        if(a[v][i] != 0 && vis[i] == -1){       
            return 1*puts("YES");      
        }  
    }      
    return 0*puts("NO");  
}

无向图:

法一:

//用父节点和子节点的转移 注意无向图父子节点是互通的

//记互通的这条边为e 那么子节点如果通过非e的边访问到“父节点”,说明有自环

//注意“父节点”可以不是直接基类,也可以是间接基类,即之前访问过的点,vis[i]=1的点

//无向无环图可以变成一棵树,理解成父向子的递归查询即可,返祖即是有环

#include <iostream>  
using namespace std;  
const int M=2005;  
bool  g[M][M],vis[M],flag;
//无权邻接矩阵 直接判有没有相连  
int   n,m;    
bool dfs(int i,int pre)  
{  
    vis[i]=1;  
    for(int j=1;j<=n;j++)  
      if(g[i][j])  
        {  
            if(!vis[j])return dfs(j,i);  //父节点与子节点同真假  
            else if(j!=pre)return false;   //如果访问过,且不是其父节点(无向图,子节点与父节点互通),那么就构成环 
        } 
}  

法二://HDU1272 小希的迷宫 补题

①判是否有自环,只需在合并两点之前判这两点是否已经来自同一个连通的集合

②判是否是连通图,只需看://这里采用的是(2)

(1)是否只有一个根节点,即是否能转化为一棵树

(2)在没有自环的情况下,边数=点数-1(树的定义)

#include <cstdio>
#include <cstring>
int par[100005];             //记录父节点
bool used[100005];         //是否使用过
void init()
{
  for(int i=1;i<100005;++i)
  par[i]=i;
}
int find(int x)
{
  return x==find(x)?x:par[x]=find(par[x]);
}
void unit(int x,int y)
{
  x=find(x);
  y=find(y);
  if (x==y)return;
  par[y]=x;
}
bool same(int x,int y)
{
  return find(x)==find(y);
}
int main()
{
   int n,m,flag,i,t;
   while (scanf("%d %d",&n,&m)!=EOF)
   {
    if (n==-1 && m==-1)break;
    if (n==0 && m==0)                      //当一开始就输入0 0的话,要输出Yes,本题坑爹处
    {
         printf ("Yes\n");
         continue;
    }
   memset(used,0,sizeof(used));
   init();
   unit(n,m); 
   used[n]=1,used[m]=1;//合并后,说明这两个点用过了
   t=1;                                              //t=点-边   刚开始时是2-1=1
   flag=1;                                           //flag表示当前所有点的根是否唯一,换言之,图是否是连通图
   while (scanf("%d %d",&n,&m)!=EOF)                 //核心部分,相当于模拟加边的过程
   {
    if (n==0 && m==0)break;
    if (used[n]==0)t++,used[n]=1;//加入一点,并标记
    if (used[m]==0)t++,used[m]=1;
    if (same(n,m))flag=0;                            //n与m在连这条边之前就已经连通
    else t--;                                        //若n和m的根节点不相同,则将他们合并,并且边数+1,相当于t-1
   }
   if (flag&&t==1)puts("Yes");                     //若图中没有环,而且点数与边数相差1,则说明该图是符合的!
   else puts("No");
   }
    return 0;
}

 

展开阅读全文

没有更多推荐了,返回首页