图的环路问题

一、无向图环路问题

题目来源: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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值