判断有向图是否有圈

1. 拓扑排序

拓扑排序是对有向无圈图的顶点的一种排序:如果存在一条vi到vj的路径,则vj排在vi后面(因为只要满足这个特性就是拓扑序列,所以它不一定是唯一的)。比如在众多的大学课程中,有些课有先修课,我们可以将其抽象为拓扑排序,有向边(v, w)表明课程v必须安排在w之前,否则课程w就无法进行。我们可以想象所有的课程以及课与课之间的关系可以用一个图来表示,而拓扑排序就可以知道课程安排的顺序。然而,如果图存在圈,就没有拓扑序列。比如如果要上课程A必须上课程B,要上课程B必须上课程C,而要上课程C必须上课程A,你将无法选择哪门课上前面。虽然有圈图没有拓扑序列,但是我们可以利用拓扑排序的算法来判断一个有向图是否有圈。

算法描述如下:

1. 将所有入度为0的顶点放入队列;
2. 每次从队列中弹出一个顶点v(即访问到该顶点,counter++)直到队列为空;
          3. 遍历所有与v相连的顶点,将相邻顶点的入度减一(删边);
          4. 若某个相邻顶点入度为0,将其放入队列中,返回第2步;
5. 若counter == N也就是所有顶点均访问到,说明排序完成。否则,说明总
    有顶点入度不为0,没有放入队列中,即该有向图有圈。

代码如下:

#include <cstdio>
#include <queue>

using namespace std;

const int MAX_N = 110;
vector<int> graph[MAX_N];  //邻接表存储图
int indegree[MAX_N];       //入度
int n;                     //顶点数
int m;                     //边数

bool TopSort()
{
    queue<int> que;
    int counter = 0;            //记录访问到的顶点数
    for (int i = 1; i <= n; i++)
    {
        if (indegree[i] == 0)   //将入度为0的顶点全部放入队列
            que.push(i);
    }
    while (!que.empty())
    {
        int v = que.front();
        que.pop();
        counter++;
        for (int i = 0; i < graph[v].size(); i++)
        {
            if (--indegree[graph[v][i]] == 0)
                que.push(graph[v][i]);
        }
    }
    if (counter != n)       //如果有圈,排序失败
        return false;
    else
        return true;
}

int main()
{

    while(scanf("%d%d",&n, &m) != EOF)
    {
        for(int i = 1; i <= n; i++)  //将图置空
            graph[i].clear();
        for(int i=0;i<m;i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            graph[u].push_back(v);
        }
        if(TopSort())
            printf("Graph does not have a cycle.\n");
        else
            printf("Graph has a cycle.\n");
    }
    return 0;
}

2. DFS

关于DFS的介绍请戳我通过稍微修改DFS,利用递归的特点,也可以判断有向图是否有圈。修改想法是把原来的visited[]只有true,false两种状态改成如下:

vis[u] = 0代表还没访问;
vis[u] = -1代表正在访问中;
vis[u] = 1代表访问完全;
如果某个点在访问过程中访问了两次,说明出现了环。
用如下样例模拟出递归过程帮助理解。

图解如下(好吧,画的有点丑,将就看吧(●‘?‘●)):
样例一(有环):
3 3
1 2
2 3
3 1


样例二(无环):
3 3
1 2
2 3
1 3

相信通过上面两幅图应该可以大致理解了,现在上代码。

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>

using namespace std;

const int MAX_N = 110;
vector<int> graph[MAX_N];
int vis[MAX_N], n, m;              //n, m分别是顶点数和边数

bool DFS(int u)
{
    vis[u] = -1;                   //-1用来表示顶点u正在访问
    for(int i = 0 ; i < graph[u].size() ; i ++)
    {
        if(vis[graph[u][i]] == -1)//表示这个点试探了两次,肯定出现了环
            return false;
        else if(vis[graph[u][i]] == 0)
        {
            if(!DFS(graph[u][i]))
                return false;
        }
    }
    vis[u] = 1;
    return true;
}

bool NoCycle()
{
    memset(vis, 0, sizeof(vis));     //初始化
    for(int i = 1 ; i <= n ; i ++)   //图可能不连通
    {
        if(!vis[i])
        {
            if(!DFS(i)) return false;
        }
    }
    return true;
}

int main()
{

    while(scanf("%d%d",&n, &m) != EOF)
    {
        for(int i=1;i<=n;i++)
            graph[i].clear();
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            graph[u].push_back(v);
        }
        if(NoCycle())
            printf("Graph does not have a cycle.\n");
        else
            printf("Graph has a cycle.\n");
    }
    return 0;
}
时间: 2024-10-12 19:39:51

判断有向图是否有圈的相关文章

查找有向图中所有圈的算法加速策略

简介 有向图G(V,E),圈是一个起始节点与终止节点相同的路径,即 a->….->a.找到所有的圈可能要遍历所有的路径,这就涉及到算法性能的考虑.本文基于深度优先搜索,讨论查找所有圈的算法加速策略. 无向图的查圈算法 深度优先搜索算法是从已知节点出发,图的一种遍历算法.只要一个节点被同源两个路径访问,这两个路径则形成一个圈.因为每个节点只处理一次,所以时间与空间复杂度都是O(N).其算法如下: DFS(a) for undirected graph stack.push(a) while no

拓扑排序判断有向图是否有回路

1 #include <iostream> 2 #include <queue> 3 #include <string> 4 using namespace std; 5 6 //表结点 7 typedef struct ArcNode{ 8 int adjvex;//该弧所指向的顶点的位置 9 ArcNode *nextarc; 10 }ArcNode; 11 12 //头结点 13 typedef struct VNode{ 14 string data;//顶点信

拓扑排序,判断有向图中是否有环

[原创] 今天我们来聊聊有向图中环的判断,在数据结构中我们知道,通过拓扑排序可以判断有向图中是否存在环,对于有向图的存储我们采用邻接表的形势,这里为了简化链表的操作,我们省略了链表,避免了指针的麻烦,直接采用了c++中的vector来模拟链表,操作更加的方便:具体详细的使用,建议百度一下,这里不多说,至于拓扑排序的具体思想,相信大家应该都了解,那么直接上代码,如果有不理解的,建议查阅数据结构书籍,搞懂思想,结合这份代码,很好理解 1 #include <stdio.h> 2 #include

hdu 1325 判断有向图是否为树

题意:判断有向图是否为树 链接:点我 这题用并查集判断连通,连通后有且仅有1个入度为0,其余入度为1,就是树了 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std;

POJ--1386--Play on Words【判断有向图欧拉通路、欧拉回路】

链接:http://poj.org/problem?id=1386 题意:要开启一扇门,n个单词是密码,n个单词中,如果一个单词的首字母和前一个单词的尾字母相同,并且每个单词都能这么连起来且只用一次,则门可以开启,否则不能开启,现给出单词,判断门是否可以开. 有向图欧拉通路充要条件:D为有向图,D的基图连通,并且所有顶点的出度与入度都相等:或者除两个顶点外,其余顶点的出度与入度都相等,而这两个顶点中一个顶点的出度与入度之差为1,另一个顶点的出度与入度之差为-1. 有向图欧拉回路充要条件:当D的所

判断有向图中是否有环

1 #include<iostream> 2 #include<malloc.h> 3 using namespace std; 4 #define maxNum 110 ///定义邻接举证的最大定点数 5 int pre[maxNum]; 6 int post[maxNum]; 7 int point=0;///pre和post的值 8 bool is_DAG=true;///标识位,表示有向无环图 9 /* 10 顶点颜色表 vis[u] 11 0 白色,未被访问过的节点标白色

Leetcode 207 课程表 (拓扑排序,判断有向图环)

定义一个队列Q,把入度为0的结点入队 若Q不为空,则取队首结点,删去所有从该点出发的边,并把这些边所到达结点的入度减一,若某个节点入度减为0,则将它入队 反复进行如上操作,直到队列为空.(当总的入队次数大于节点数时,跳出循环) 如果这时入过队的节点数恰好等于节点总数,则为有向无环图.否则有环. class Solution { public: static const int maxn = 10000; vector<int> gra[maxn]; int co = 0; int count[

判断有向图中是否存在环

1 // 将先修关系构成一张图,由每个数对的第二个数字向第一个数字连边. 2 // 首先将所有入度为0的点进队,准备拓扑排序. 3 // 宽搜过程中,将当前结点所关联的结点的入度减1:若发现新的入度为0的结点,则将其进队. 4 // 最后如果遍历了所有结点,则说明可以满足要求:否则,先修关系存在环. 5 6 //查找是否有环 7 class Solution 8 { 9 public: 10 bool canFinish(int numCourses, vector<vector<int>

CSUOJ-1978 LXX的图论题(最短路、Bellman-Ford判断负圈)

1978: LXX的图论题 Time Limit: 1 Sec     Memory Limit: 128 Mb     Submitted: 22     Solved: 9 Description 由于lxx的图论和数据结构太弱了,大佬Z决定为lxx补一补.于是大佬Z为lxx出了一道题目,题目如下:给出一张有向图,图中有n个点,m条边,每条边上都有一个权值w,问图中是否存在满足以下条件的点i,j,...p使得不等式w[i][j] * w[j][k] * .... * w[p][i]<1成立.