【C++】判断一个图是否有环 无向图 有向图(转载)
没有找到原文出处,请参考一下链接:
http://www.cnblogs.com/hiside/archive/2010/12/01/1893878.html
http://topic.csdn.net/u/20071023/11/3edb81fc-37b2-4506-906e-44dc0fc521f2.html
一、无向图:
方法1:
- 如果存在回路,则必存在一个子图,是一个环路。环路中所有顶点的度>=2。
- n算法:
第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一。
第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一。
如果最后还有未删除顶点,则存在环,否则没有环。
- n算法分析:
由于有m条边,n个顶点。
i)如果m>=n,则根据图论知识可直接判断存在环路。(证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,边的数目m = n-k。k>=1,所以:m<n)
ii)如果m<n 则按照上面的算法每删除一个度为0的顶点操作一次(最多n次),或每删除一个度为1的顶点(同时删一条边)操作一次(最多m次)。这两种操作的总数不会超过m+n。由于m<n,所以算法复杂度为O(n)。
- 注:
该方法,算法复杂度不止O(V),首先初始时刻统计所有顶点的度的时候,复杂度为(V + E),即使在后来的循环中E>=V,这样算法的复杂度也只能为O(V + E)。其次,在每次循环时,删除度为1的顶点,那么就必须将与这个顶点相连的点的度减一,并且执行delete node from list[list[node]],这里查找的复杂度为list[list[node]]的长度,只有这样才能保证当degree[i]=1时,list[i]里面只有一个点。这样最差的复杂度就为O(EV)了。
方法2:
DFS搜索图,图中的边只可能是树边或反向边,一旦发现反向边,则表明存在环。该算法的复杂度为O(V)。
方法3:
摘自:http://blog.csdn.net/lzrzhao/archive/2008/03/13/2175787.aspx
PS:此方法于2011-6-12补充
假定:图顶点个数为M,边条数为E
原图就有环
将P个连通分量的不等式相加,就得到:
即: E + P > M 所以只要判断结果 E + P > M 就表示原图有环,否则无环.
实例代码如下:
- #include<iostream>
- #include<malloc.h>
- using namespace std;
- #define maxNum 100 //定义邻接举证的最大定点数
- int visited[maxNum];//通过visited数组来标记这个顶点是否被访问过,0表示未被访问,1表示被访问
- int DFS_Count;//连通部件个数,用于测试无向图是否连通,DFS_Count=1表示只有一个连通部件,所以整个无向图是连通的
- int pre[maxNum];
- int post[maxNum];
- int point;//pre和post的值
- //图的邻接矩阵表示结构
- typedef struct
- {
- char v[maxNum];//图的顶点信息
- int e[maxNum][maxNum];//图的顶点信息
- int vNum;//顶点个数
- int eNum;//边的个数
- }graph;
- void createGraph(graph *g);//创建图g
- void DFS(graph *g);//深度优先遍历图g
- void dfs(graph *g,int i);//从顶点i开始深度优先遍历与其相邻的点
- void dfs(graph *g,int i)
- {
- //cout<<"顶点"<<g->v[i]<<"已经被访问"<<endl;
- cout<<"顶点"<<i<<"已经被访问"<<endl;
- visited[i]=1;//标记顶点i被访问
- pre[i]=++point;
- for(int j=1;j<=g->vNum;j++)
- {
- if(g->e[i][j]!=0&&visited[j]==0)
- dfs(g,j);
- }
- post[i]=++point;
- }
- void DFS(graph *g)
- {
- int i;
- //初始化visited数组,表示一开始所有顶点都未被访问过
- for(i=1;i<=g->vNum;i++)
- {
- visited[i]=0;
- pre[i]=0;
- post[i]=0;
- }
- //初始化pre和post
- point=0;
- //初始化连通部件数为0
- DFS_Count=0;
- //深度优先搜索
- for(i=1;i<=g->vNum;i++)
- {
- if(visited[i]==0)//如果这个顶点为被访问过,则从i顶点出发进行深度优先遍历
- {
- DFS_Count++;//统计调用void dfs(graph *g,int i);的次数
- dfs(g,i);
- }
- }
- }
- void createGraph(graph *g)//创建图g
- {
- cout<<"正在创建无向图..."<<endl;
- cout<<"请输入顶点个数vNum:";
- cin>>g->vNum;
- cout<<"请输入边的个数eNum:";
- cin>>g->eNum;
- int i,j;
- //输入顶点信息
- //cout<<"请输入顶点信息:"<<endl;
- //for(i=0;i<g->vNum;i++)
- // cin>>g->v[i];
- //初始画图g
- for(i=1;i<=g->vNum;i++)
- for(j=1;j<=g->vNum;j++)
- g->e[i][j]=0;
- //输入边的情况
- cout<<"请输入边的头和尾"<<endl;
- for(int k=0;k<g->eNum;k++)
- {
- cin>>i>>j;
- g->e[i][j]=1;
- g->e[j][i]=1;//无向图对称
- }
- }
- int main()
- {
- graph *g;
- g=(graph*)malloc(sizeof(graph));
- createGraph(g);//创建图g
- DFS(g);//深度优先遍历
- //连通部件数,用于判断是否连通图
- cout<<"连通部件数量:";
- cout<<DFS_Count<<endl;
- if(DFS_Count==1)
- cout<<"图g是连通图"<<endl;
- else if(DFS_Count>1)
- cout<<"图g不是连通图"<<endl;
- //各顶点的pre和post值
- for(int i=1;i<=g->vNum;i++)
- cout<<"顶点"<<i<<"的pre和post分别为:"<<pre[i]<<" "<<post[i]<<endl;
- //cout<<endl;
- //判断无向图中是否有环
- if(g->eNum+DFS_Count>g->vNum)
- cout<<"图g中存在环"<<endl;
- else
- cout<<"图g中不存在环"<<endl;
- int k;
- cin>>k;
- return 0;
- }
- /*
- 输入:
- 正在创建无向图...
- 请输入顶点个数vNum:10
- 请输入边的个数eNum:9
- 请输入边的头和尾
- 1 2
- 1 4
- 2 5
- 2 6
- 4 7
- 5 9
- 6 3
- 7 8
- 9 10
- */
注意:有向图不能使用此方法。比如1->2,1-3,2->3,4->5,如果使用上述方法会判定为含有还,但并非如此。
有向图:
主要有深度优先和拓扑排序2中方法
1、拓扑排序,如果能够用拓扑排序完成对图中所有节点的排序的话,就说明这个图中没有环,而如果不能完成,则说明有环。
2、可以用Strongly Connected Components来做,我们可以回忆一下强连通子图的概念,就是说对于一个图的某个子图,该子图中的任意u->v,必有v->u,则这是一个强连通子图。这个限定正好是环的概念。所以我想,通过寻找图的强连通子图的方法应该可以找出一个图中到底有没有环、有几个环。
3、就是用一个改进的DFS
刚看到这个问题的时候,我想单纯用DFS就可以解决问题了。但细想一下,是不能够的。如果题目给出的是一个无向图,那么OK,DFS是可以解决的。但无向图得不出正确结果的。比如:A->B,A->C->B,我们用DFS来处理这个图,我们会得出它有环,但其实没有。
我们可以对DFS稍加变化,来解决这个问题。解决的方法如下:
图中的一个节点,根据其C[N]的值,有三种状态:
0,此节点没有被访问过
-1,被访问过至少1次,其后代节点正在被访问中
1,其后代节点都被访问过。
按照这样的假设,当按照DFS进行搜索时,碰到一个节点时有三种可能:
1、如果C[V]=0,这是一个新的节点,不做处理
2、如果C[V]=-1,说明是在访问该节点的后代的过程中访问到该节点本身,则图中有环。
3、如果C[V]=1,类似于2的推导,没有环。 在程序中加上一些特殊的处理,即可以找出图中有几个环,并记录每个环的路径
PS:此代码实现于2011-6-13补充
改进DFS算法代码示例(判断是否是一个有向无环图)
- #include<iostream>
- #include<malloc.h>
- using namespace std;
- #define maxNum 100 //定义邻接举证的最大定点数
- int pre[maxNum];
- int post[maxNum];
- int point=0;//pre和post的值
- bool is_DAG=true;//标识位,表示有向无环图
- /*
- 顶点颜色表 color[u]
- 0 白色,未被访问过的节点标白色
- -1 灰色,已经被访问过一次的节点标灰色
- 1 黑色,当该节点的所有后代都被访问过标黑色
- 反向边:
- 如果第一次访问(u,v)时v为灰色,则(u,v)为反向边。在对图的深度优先搜索中没有发现
- 反向边,则该图没有回路
- 程序判断依据:
- 仍然是按图的节点深度遍历,访问到V时,V若被访问过,那么有2种状态:
- color[u]=-1,程序跳出,存在环
- color[u]=1,程序继续,这不是环
- 时间复杂度:O(n+e)
- */
- int color[maxNum];//顶点颜色表 color[u]
- //图的邻接矩阵表示结构
- typedef struct
- {
- char v[maxNum];//图的顶点信息
- int e[maxNum][maxNum];//图的顶点信息
- int vNum;//顶点个数
- int eNum;//边的个数
- }graph;
- void createGraph(graph *g);//创建图g
- void DFS(graph *g);//深度优先遍历图g
- void dfs(graph *g,int i);//从顶点i开始深度优先遍历与其相邻的点
- void dfs(graph *g,int i)
- {
- //cout<<"顶点"<<g->v[i]<<"已经被访问"<<endl;
- cout<<"顶点"<<i<<"已经被访问"<<endl;
- color[i]=-1;
- pre[i]=++point;
- for(int j=1;j<=g->vNum;j++)
- {
- if(g->e[i][j]!=0)
- {
- if(color[j]==-1)//探索到回边,存在环
- {
- is_DAG=false;//不是有向无环图
- }
- else if(color[j]==0)
- dfs(g,j);
- }
- }
- post[i]=++point;
- color[i]=1;//表示i的后裔节点都被访问过
- }
- void DFS(graph *g)
- {
- int i;
- //初始化color数组,表示一开始所有顶点都未被访问过,//初始化pre和post
- for(i=1;i<=g->vNum;i++)
- {
- color[i]=0;
- pre[i]=0;
- post[i]=0;
- }
- //深度优先搜索
- for(i=1;i<=g->vNum;i++)
- {
- if(color[i]==0)//如果这个顶点为被访问过,则从i顶点出发进行深度优先遍历
- {
- dfs(g,i);
- }
- }
- }
- void createGraph(graph *g)//创建图g
- {
- cout<<"正在创建无向图..."<<endl;
- cout<<"请输入顶点个数vNum:";
- cin>>g->vNum;
- cout<<"请输入边的个数eNum:";
- cin>>g->eNum;
- int i,j;
- //初始画图g
- for(i=1;i<=g->vNum;i++)
- for(j=1;j<=g->vNum;j++)
- g->e[i][j]=0;
- //输入边的情况
- cout<<"请输入边的头和尾"<<endl;
- for(int k=1;k<=g->eNum;k++)
- {
- cin>>i>>j;
- g->e[i][j]=1;
- }
- }
- int main()
- {
- graph *g;
- g=(graph*)malloc(sizeof(graph));
- createGraph(g);//创建图g
- DFS(g);//深度优先遍历
- //各顶点的pre和post值
- for(int i=1;i<=g->vNum;i++)
- cout<<"顶点"<<i<<"的pre和post分别为:"<<pre[i]<<" "<<post[i]<<endl;
- //判断是否是有向无环图
- if(is_DAG)
- cout<<"图g是有向无环图,没有环"<<endl;
- else
- cout<<"图g不是有向无环图,存在环"<<endl;
- int k;
- cin>>k;
- return 0;
- }
- /*
- 输入1:
- 正在创建无向图...
- 请输入顶点个数vNum:3
- 请输入边的个数eNum:3
- 请输入边的头和尾
- 1 2
- 1 3
- 3 2
- 输入2:
- 正在创建无向图...
- 请输入顶点个数vNum:4
- 请输入边的个数eNum:4
- 请输入边的头和尾
- 1 2
- 2 3
- 3 4
- 4 2
- */
【C++】判断一个图是否有环 无向图 有向图(转载)的更多相关文章
- poj2762 判断一个图中任意两点是否存在可达路径 也可看成DAG的最小覆盖点是否为1
Going from u to v or from v to u? Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 179 ...
- 图论期末大作业编程题(如何判断一个4连通4正则图为无爪、无K4图)
博士期间估计这可能是唯一一个要编程的作业,搞了半天弄出这个东西,放这里为以后用到的时候查找方便. 说来也是可笑,读博士期间发现大家对上课也都没什么兴趣,老师也是那么回事,都说博士期间学的课程是要有助于 ...
- Geeks - Detect Cycle in a Directed Graph 推断图是否有环
Detect Cycle in a Directed Graph 推断一个图是否有环,有环图例如以下: 这里唯一注意的就是,这是个有向图, 边组成一个环,不一定成环,由于方向能够不一致. 这里就是添加 ...
- 判断单链表是否有环,并找出环的入口python
1.如何判断一个链表是否有环? 2.如果链表为存在环,如果找到环的入口点? 1.限制与要求 不允许修改链表结构. 时间复杂度O(n),空间复杂度O(1). 2.思考 2.1判断是否有环 如果链表有环, ...
- DFS判断图是否有环
利用_DFS_来判断无向图是否存在环的条件思路,我看一次_DFS_是否能访问到之前访问到的节点,如果能够访问到,就说明图存在环,那么关键问题就是判断是一次DFS?,追根到_DFS_算法的实现细节, ...
- HDU4514(非连通图的环判断与图中最长链)
题目:设计风景线 题意:给定一个无向图,图可能是非连通的,如果图中存在环,就输出YES,否则就输出图中最长链的长度. 分析:首先我们得考虑这是一个无向图,而且有可能是非连通的,那么就不能直接像求树那样 ...
- ZOJ 1015 Fishing Net(判断弦图)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=15 题意:给定一个图.判断是不是弦图? 思路:(1)神马是弦图?对于一 ...
- 图结构练习——判断给定图是否存在合法拓扑序列(dfs算法(第一个代码),邻接矩阵(前两个代码),邻接表(第三个代码))
sdut 2140 图结构练习——判断给定图是否存在合法拓扑序列 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 给定一个有向图 ...
- HDU 3594 Cactus (强连通分量 + 一个边只能在一个环里)
题意:判断题目中给出的图是否符合两个条件.1 这图只有一个强连通分量 2 一条边只能出现在一个环里. 思路:条件1的满足只需要tarjan算法正常求强连通分量即可,关键是第二个条件,我们把对边的判断转 ...
随机推荐
- java mybatis 参数问题
- Spring+quartz集群解决多服务器部署定时器重复执行的问题
一.问题描述 Spring自带的Task虽然能很好使用定时任务,只需要做些简单的配置就可以了.不过如果部署在多台服务器上的时候,这样定时任务会在每台服务器都会执行,造成重复执行. 二.解决方案 Spr ...
- Chrome 地址栏如何设置显示 http/https 和 www
首先在 chrome 地址栏输入以下地址 chrome://flags/#omnibox-ui-hide-steady-state-url-scheme-and-subdomains 然后使用 Ctr ...
- springboot整合mybatis通用Mapper
参考: https://blog.csdn.net/x18707731829/article/details/82814095 https://www.jianshu.com/p/6d2103451d ...
- BTC功能类
<?php/*EasyBitcoin-PHP A simple class for making calls to Bitcoin's API using PHP.https://github. ...
- ssh实现免密码登录和文件传输
一般的用户名密码认证不安全,很容易被暴力破解,还不方便:而大多数人都是选择使用 SSH 密钥认证,不仅安全还不用每次输密码 ssh密钥对 # 使用 ssh-keygen 生成非对称密钥,一路回车即可 ...
- 万能的pdftk
pdftk (the pdf toolkit) 是一个功能强大的命令行的 PDF 文件编辑软件,可以合并/分割 PDF 文档.对 PDF 文件加密解密.给 PDF 文档加水印.从 PDF 文档中解出附 ...
- MySQL系列(五)--二进制日志对主从复制的影响
MySQL复制是基于主库上的二进制日志来完成,复制是异步的,可能存在延迟 MySQL日志分为: 1.服务层日志:二进制日志.通用日志.慢查日志 2.存储引擎层日志:innodb中重做日志和回滚日志 二 ...
- 洛谷P3298 泉
时空限制 1000ms / 128MB 题目描述 作为光荣的济南泉历史研究小组中的一员,铭铭收集了历史上x个不同年份时不同泉区的水流指数,这个指数是一个小于. 2^30的非负整数.第i个年份时六个泉区 ...
- 几个树形dp
1.重建道路 树形dp基础题,f[i][j]表示在i这个点我和我的子树联通块大小为j最少砍几条边. 转移的时候,到下一个子树时上一个子树所有答案先++(此树直接砍掉不贡献答案),再继续dp. 注意更新 ...