一、无向图环路问题
题目来源:http://acm.buaa.edu.cn/contest/141/problem/F/
网上搜了一下,目测有不下三种解决方法。不过这里只介绍两种方法来求解无向图的环路问题。
1.利用DFS求解
并不是说,访问过程中遇到与所要查找的节点相同就说明存在回路。因为有可能查找过程中遇到的情况可能是1->2,2->1。为此,设置一个前驱节点,当当前节点已经被访问过(说明之前访问了该节点)且该节点不等于前驱节点时,无向图存在环路。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MaxSize 10010
typedef struct node
{
int data;
node* nextEdge;
}EdgeNode;
typedef struct
{
EdgeNode* firstEdge;
}HeadNode;
typedef struct
{
HeadNode adjList[MaxSize];
}Graph;
int n,m;
int u,v;
int visited[MaxSize];
int ans;
void DFS(Graph* g,int x,int pre)
{
if(ans==1)
return;
visited[x]=1;
EdgeNode* p=g->adjList[x].firstEdge;
while(p!=NULL)
{
if(visited[p->data]==0)
DFS(g,p->data,x);
else
{
if(p->data!=pre)
ans=1;
}
p=p->nextEdge;
}
}
void DestroyGraph(Graph* &g)
{
int i;
for(i=1;i<=n;i++)
{
if(g->adjList[i].firstEdge!=NULL)
{
EdgeNode* pre=g->adjList[i].firstEdge;
EdgeNode* p=pre->nextEdge;
while(p!=NULL)
{
free(pre);
pre=p;
p=p->nextEdge;
}
free(pre);
}
}
free(g);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(visited,0,sizeof(visited));
Graph* g=(Graph*)malloc(sizeof(Graph));
int i;
for(i=1;i<=n;i++)
g->adjList[i].firstEdge=NULL;
while(m--)
{
scanf("%d%d",&u,&v);
EdgeNode* p=(EdgeNode*)malloc(sizeof(EdgeNode));
p->data=v;
p->nextEdge=g->adjList[u].firstEdge;
g->adjList[u].firstEdge=p;
EdgeNode* q=(EdgeNode*)malloc(sizeof(EdgeNode));
q->data=u;
q->nextEdge=g->adjList[v].firstEdge;
g->adjList[v].firstEdge=q;
}
ans=0;
for(i=1;i<=n;i++)
{
if(visited[i]==0)
DFS(g,i,0);
}
puts(ans?"Yes":"No");
DestroyGraph(g);
}
}
2.利用并查集求解
并查集的思路比较简单,也更容易接受。求解思想是,首先合并集合。查询时判断两点是否属于同一个集合(已经除去自环以及重边情况),若属于同一集合,则说明存在环。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MaxSize 10010
//并查集数据结构定义
typedef struct
{
int rank;
int parent;
}UFSTree;
int n,m;
int u,v;
UFSTree t[MaxSize];
void InitUFSTree(UFSTree t[])
{
int i;
for(i=1;i<=MaxSize;i++)
{
t[i].rank=0;
t[i].parent=i;
}
}
int FindSet(UFSTree t[],int x)
{
if(x!=t[x].parent)
return FindSet(t,t[x].parent);
else
return x;
}
//合并集合
void Union(UFSTree t[],int u,int v)
{
if(t[u].rank>t[v].rank)
{
t[v].parent=u;
}
else
{
t[u].parent=v;
if(t[u].rank==t[v].rank)
t[v].rank++;
}
}
bool HasLoopPath(UFSTree t[],int u,int v)
{
bool flag=false;
u=FindSet(t,u);
v=FindSet(t,v);
if(u==v)//属于同一个集合
flag=true;
else
Union(t,u,v);
return flag;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
bool flag=false;
InitUFSTree(t);
while(m--)
{
scanf("%d%d",&u,&v);
if(HasLoopPath(t,u,v))
flag=HasLoopPath(t,u,v);
}
puts(flag?"Yes":"No");
}
**
二、有向图环路问题
---------
问题来源:http://acm.buaa.edu.cn/contest/141/problem/G/
**
有向图环路问题也采用DFS求解。不过不同的是,访问数组visited[]需要增加一种情况。由于遍历到的已访问过的点不一定说明成环,只有当这个点是这次遍历的祖先节点时才成环,所以记录状态时要多一个状态:0表示尚未访问,-1表示正在访问子节点,1表示已访问过所有子节点。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MaxSize 10010
typedef struct node
{
int data;
node* nextEdge;
}EdgeNode;
typedef struct
{
EdgeNode* firstEdge;
}HeadNode;
typedef struct
{
HeadNode adjList[MaxSize];
}Graph;
int n,m;
int u,v;
int visited[MaxSize];
bool DFSFindLoopPath(Graph* g,int u)
{
if(visited[u]==-1)
return true;
visited[u]=-1;
EdgeNode* p=g->adjList[u].firstEdge;
while(p!=NULL)
{
if(visited[p->data]==0)
return DFSFindLoopPath(g,p->data);
else if(visited[p->data]==-1)
return true;
p=p->nextEdge;
}
visited[u]=1;
return false;
}
void DestroyGraph(Graph* &g)
{
int i;
for(i=1;i<=n;i++)
{
if(g->adjList[i].firstEdge!=NULL)
{
EdgeNode* pre=g->adjList[i].firstEdge;
EdgeNode* p=pre->nextEdge;
while(p!=NULL)
{
free(pre);
pre=p;
p=p->nextEdge;
}
free(pre);
}
}
free(g);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(visited,0,sizeof(visited));
Graph* g=(Graph*)malloc(sizeof(Graph));
int i;
for(i=1;i<=n;i++)
g->adjList[i].firstEdge=NULL;
while(m--)
{
scanf("%d%d",&u,&v);
EdgeNode* p=(EdgeNode*)malloc(sizeof(EdgeNode));
p->data=v;
p->nextEdge=g->adjList[u].firstEdge;
g->adjList[u].firstEdge=p;
}
bool flag=false;
for(i=1;i<=n;i++)
{
if(visited[i]==0)
flag=DFSFindLoopPath(g,i);
}
puts(flag?"Yes":"No");
DestroyGraph(g);
}
}