判断有向图是否存在环、环的个数、环的元素

来源:互联网 发布:java 形参 实参 编辑:程序博客网 时间:2024/06/05 09:00

判断有向图是否有环有三种方法:拓扑排序、深度遍历+回溯、深度遍历 + 判断后退边

这里使用 拓扑排序 和 深度遍历 + 回溯判断是不是环。使用 深度遍历 + 判断后退边找出环个数 以及环中元素

1、拓扑排序

思想:找入度为0的顶点,输出顶点,删除出边。循环到无顶点输出。

若:输出所有顶点,则课拓扑排序,无环;反之,则不能拓扑排序,有环

使用:可以使用拓扑排序为有向无环图每一个结点进行编号,拓扑排序输出的顺序可以为编号顺序

源代码:

[cpp] view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3. const int MAX_Vertex_Num = 20;  
  4. template<class VexType,class ArcType>  
  5. class MGraph  
  6. {  
  7. public:  
  8.     void CreateGraph();//创建图  
  9.     int LocateVex(VexType v);//返回顶点v所在顶点向量中的位置(下标)  
  10.     void CheckCircle();  
  11. private:  
  12.     VexType vexs[MAX_Vertex_Num];//顶点向量  
  13.     ArcType arcs[MAX_Vertex_Num][MAX_Vertex_Num]; //这里把邻接矩阵类型用模板表示,主要是为了处理有权值的情况,比如:权值可以为小数(代价),也可以为整数  
  14.     int vexnum;//顶点数  
  15.     int arcnum;//边数  
  16. private:  
  17.     bool TopSort();  
  18. };  
  19.   
  20. template<class VexType,class ArcType>  
  21. void MGraph<VexType,ArcType>::CreateGraph()  
  22. {  
  23.     VexType first;  
  24.     VexType Secend;  
  25.     cout<<"请输入顶点数:";  
  26.     cin>>vexnum;  
  27.     cout<<"请输入边数:";  
  28.     cin>>arcnum;  
  29.     cout<<"请输入各个顶点值:";  
  30.     for (int i=0;i<vexnum;i++)  
  31.     {  
  32.         cin>>vexs[i];  
  33.     }  
  34.     //初始化邻接矩阵  
  35.     for (int i=0;i<arcnum;i++)  
  36.     {  
  37.         for (int j=0;j<arcnum;j++)  
  38.         {  
  39.             arcs[i][j]=0;  
  40.         }  
  41.     }  
  42.     cout<<"请输入边的信息:"<<endl;  
  43.     for (int i=0;i<arcnum;i++)  
  44.     {  
  45.         cin>>first>>Secend;  
  46.         //如果边有权值的话,则还应该输入权值  
  47.         int x = LocateVex(first);  
  48.         int y = LocateVex(Secend);  
  49.         arcs[x][y]=1;//如果是有权的话,这里应该是arc[x][y]=权值  
  50.     }  
  51. }   
  52. /* 
  53. 参数:v:表示顶点向量中一个值 
  54. 函数返回值:函数返回v在顶点向量中的下标 
  55. */  
  56. template<class VexType,class ArcType>  
  57. int MGraph<VexType,ArcType>::LocateVex(VexType v)  
  58. {  
  59.     for (int i=0;i<vexnum;i++)  
  60.     {  
  61.         if (vexs[i]==v)  
  62.         {  
  63.             return i;  
  64.         }  
  65.     }  
  66.     return -1;  
  67. }  
  68. /* 
  69. 有向图可以拓扑排序的条件是:图中没有环。 
  70. 具体方法: 
  71. ⑴ 从图中选择一个入度为0的点加入拓扑序列。 
  72. ⑵ 从图中删除该结点以及它的所有出边(即与之相邻点入度减1)。 
  73.  
  74. */  
  75. template<class VexType,class ArcType>  
  76. bool MGraph<VexType,ArcType>::TopSort()  
  77. {  
  78.     int count = 0;//拓扑排序输出顶点的个数  
  79.     int top = -1;  
  80.     int stack[MAX_Vertex_Num];  
  81.     int indegree[MAX_Vertex_Num]={0};  
  82.     //求各个顶点的入度--邻接矩阵要查询该元素的列(记录入度情况)--  
  83.     //如果是邻接表,就是麻烦在这里,查询结点入度很不方便  
  84.     for (int i=0;i<vexnum;i++)  
  85.     {  
  86.         int num=0;  
  87.         for (int j=0;j<vexnum;j++)  
  88.         {  
  89.             if (arcs[j][i]!=0)  
  90.             {  
  91.                 num++;  
  92.             }  
  93.         }  
  94.         indegree[i]=num;  
  95.     }  
  96.     //把入度为0的顶点入栈  
  97.     for (int i=0;i<vexnum;i++)  
  98.     {  
  99.         if (!indegree[i])  
  100.         {  
  101.             stack[++top]=i;//顶点的下标  
  102.         }  
  103.     }  
  104.     //处理入度为0的结点:把入度为0的结点出栈,删除与之有关的边  
  105.     while (top>-1)  
  106.     {  
  107.         int x = stack[top--];  
  108.         cout<<vexs[x];  
  109.         count++;  
  110.         //把与下标为x的顶点有关的边都去掉(出边),并改变对应结点的入度  
  111.         for (int i=0;i<vexnum;i++)  
  112.         {  
  113.             if (arcs[x][i]!=0)  
  114.             {  
  115.                 arcs[x][i]=0;//删除到下标为i的顶点的边,这时此顶点的入度减一  
  116.                 indegree[i]--;  
  117.                 if (!indegree[i])//顶点的入度为0,则入栈  
  118.                 {  
  119.                     stack[++top]=i;  
  120.                 }  
  121.             }  
  122.         }  
  123.     }  
  124.     cout<<endl;  
  125.     if (count == vexnum) //能拓扑排序  
  126.     {  
  127.         return true;  
  128.     }  
  129.     return false;  
  130. }  
  131. /* 
  132. 检查图中是不是有环 
  133. 思想: 
  134. 能进行拓扑排序,则无环,反之有环 
  135. */  
  136. template<class VexType,class ArcType>  
  137. void MGraph<VexType,ArcType>::CheckCircle()  
  138. {  
  139.     if (TopSort())  
  140.     {  
  141.         cout<<"无环!"<<endl;  
  142.     }  
  143.     else  
  144.     {  
  145.         cout<<"有环!"<<endl;  
  146.     }  
  147. }  
  148.   
  149. int main()  
  150. {  
  151.     MGraph<char,int> G;  
  152.     G.CreateGraph();  
  153.     G.CheckCircle();  
  154.     system("pause");  
  155.     return 1;  
  156. }  

测试:

有向图:

 

结果:

2、深度遍历 + 回溯

思想:用回溯法,遍历时,如果遇到了之前访问过的结点,则图中存在环。

代码:

[cpp] view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3. const int MAX_Vertex_Num = 20;  
  4. template<class VexType,class ArcType>  
  5. class MGraph  
  6. {  
  7. public:  
  8.     void CreateGraph();//创建图  
  9.     int LocateVex(VexType v);//返回顶点v所在顶点向量中的位置(下标)  
  10.     bool CheckCircle();//检查图中有无环  
  11. private:  
  12.     VexType vexs[MAX_Vertex_Num];//顶点向量  
  13.     ArcType arcs[MAX_Vertex_Num][MAX_Vertex_Num]; //这里把邻接矩阵类型用模板表示,主要是为了处理有权值的情况,比如:权值可以为小数(代价),也可以为整数  
  14.     int vexnum;//顶点数  
  15.     int arcnum;//边数  
  16. private:  
  17.     void CheckCircle(int u,bool& isExist,bool visited[MAX_Vertex_Num],bool Isvisited[MAX_Vertex_Num]);  
  18. };  
  19.   
  20. template<class VexType,class ArcType>  
  21. void MGraph<VexType,ArcType>::CreateGraph()  
  22. {  
  23.     VexType first;  
  24.     VexType Secend;  
  25.     cout<<"请输入顶点数:";  
  26.     cin>>vexnum;  
  27.     cout<<"请输入边数:";  
  28.     cin>>arcnum;  
  29.     cout<<"请输入各个顶点值:";  
  30.     for (int i=0;i<vexnum;i++)  
  31.     {  
  32.         cin>>vexs[i];  
  33.     }  
  34.     //初始化邻接矩阵  
  35.     for (int i=0;i<arcnum;i++)  
  36.     {  
  37.         for (int j=0;j<arcnum;j++)  
  38.         {  
  39.             arcs[i][j]=0;  
  40.         }  
  41.     }  
  42.     cout<<"请输入边的信息:"<<endl;  
  43.     for (int i=0;i<arcnum;i++)  
  44.     {  
  45.         cin>>first>>Secend;  
  46.         //如果边有权值的话,则还应该输入权值  
  47.         int x = LocateVex(first);  
  48.         int y = LocateVex(Secend);  
  49.         arcs[x][y]=1;//如果是有权的话,这里应该是arc[x][y]=权值  
  50.     }  
  51. }   
  52. /* 
  53. 参数:v:表示顶点向量中一个值 
  54. 函数返回值:函数返回v在顶点向量中的下标 
  55. */  
  56. template<class VexType,class ArcType>  
  57. int MGraph<VexType,ArcType>::LocateVex(VexType v)  
  58. {  
  59.     for (int i=0;i<vexnum;i++)  
  60.     {  
  61.         if (vexs[i]==v)  
  62.         {  
  63.             return i;  
  64.         }  
  65.     }  
  66.     return -1;  
  67. }  
  68.   
  69. /* 
  70. 思想:用回溯法,遍历时,如果遇到了之前访问过的结点,则图中存在环。 
  71. 引入visited数组的原因: 
  72.    1)在一次深度遍历时,检测路径上是否结点是否已经被检测到,如果被重复检测,则表示有环。 
  73.    2)注意,在深度递归返回时,总是要把visited置为false。 
  74. 引入Isvisited数组的原因: 
  75.    1)用于记录目前为止深度遍历过程中遇到的顶点。 
  76.    2)因为,我们不一定以所有结点为起始点都进行一次深度遍历。 
  77.    3)举例,在结点A为起点,进行深度遍历时,遇到了结点B,此时Isvisited在A和B两个位置都为true。 
  78.    那么没遇到环,那么我们就不用再以B为起始点继续进行一次深度遍历了, 
  79.    因为A为起点的深度遍历已经验证不会有环了。       
  80. */  
  81. template<class VexType,class ArcType>  
  82. void MGraph<VexType,ArcType>::CheckCircle(int u,bool& isExist,bool visited[MAX_Vertex_Num],bool Isvisited[MAX_Vertex_Num])  
  83. {  
  84.     visited[u]=true;  
  85.     Isvisited[u]=true;  
  86.     for (int j=0;j<vexnum;j++)  
  87.     {  
  88.         if (arcs[u][j]==1)  
  89.         {  
  90.             if (visited[j]==false)  
  91.             {  
  92.                 CheckCircle(j,isExist,visited,Isvisited);  
  93.             }  
  94.             else  
  95.             {  
  96.                 isExist = true;  
  97.             }  
  98.         }  
  99.     }  
  100.     visited[u]=false;//回溯,如果不写就变成一半的深度遍历,不能进行判断是否有边存在  
  101. }  
  102.   
  103. template<class VexType,class ArcType>  
  104. bool MGraph<VexType,ArcType>::CheckCircle()  
  105. {  
  106.     bool isExist = false;  
  107.     bool Isvisited[MAX_Vertex_Num]={false};  
  108.     bool visited[MAX_Vertex_Num]={false};  
  109.     for (int i=0;i<vexnum;i++)  
  110.     {  
  111.         if (Isvisited[i]==false)  
  112.         {  
  113.             CheckCircle(i,isExist,visited,Isvisited);  
  114.             if (isExist)  
  115.             {  
  116.                 return true;  
  117.             }  
  118.         }  
  119.     }  
  120.     return isExist;  
  121. }  
  122.   
  123. int main()  
  124. {  
  125.     MGraph<char,int> G;  
  126.     G.CreateGraph();  
  127.     if (G.CheckCircle())  
  128.     {  
  129.         cout<<"图存在环!"<<endl;  
  130.     }  
  131.     else  
  132.     {  
  133.         cout<<"图不存在环!"<<endl;  
  134.     }  
  135.     system("pause");  
  136.     return 1;  
  137. }  

结果测试:

图:

 

结果:

3、深度遍历 + 判断后退边

思想:用DFS(深度优先遍历),判断是否有后退边,若有,则存在环

具体来说,在遍历顶点的每一条边时,判断一下这个边的顶点是不是在栈中,如果在栈中,说明之前已经访问过了,这里再次访问,说明有环存在

判断后退边时,借助一个栈和一个数组

栈:即可以用来输出环

数组:inStack判断是否在栈中

源代码:

[cpp] view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3. const int MAX_Vertex_Num = 20;  
  4. template<class VexType,class ArcType>  
  5. class MGraph  
  6. {  
  7. public:  
  8.     void CreateGraph();//创建图  
  9.     int LocateVex(VexType v);//返回顶点v所在顶点向量中的位置(下标)  
  10.     void CheckCircle();  
  11. private:  
  12.     VexType vexs[MAX_Vertex_Num];//顶点向量  
  13.     ArcType arcs[MAX_Vertex_Num][MAX_Vertex_Num]; //这里把邻接矩阵类型用模板表示,主要是为了处理有权值的情况,比如:权值可以为小数(代价),也可以为整数  
  14.     int vexnum;//顶点数  
  15.     int arcnum;//边数  
  16. private:  
  17.     void DFS(int x,bool visited[MAX_Vertex_Num],int stack[MAX_Vertex_Num],int& top,bool inStack[MAX_Vertex_Num],int& count);  
  18.   
  19. };  
  20.   
  21. template<class VexType,class ArcType>  
  22. void MGraph<VexType,ArcType>::CreateGraph()  
  23. {  
  24.     VexType first;  
  25.     VexType Secend;  
  26.     cout<<"请输入顶点数:";  
  27.     cin>>vexnum;  
  28.     cout<<"请输入边数:";  
  29.     cin>>arcnum;  
  30.     cout<<"请输入各个顶点值:";  
  31.     for (int i=0;i<vexnum;i++)  
  32.     {  
  33.         cin>>vexs[i];  
  34.     }  
  35.     //初始化邻接矩阵  
  36.     for (int i=0;i<arcnum;i++)  
  37.     {  
  38.         for (int j=0;j<arcnum;j++)  
  39.         {  
  40.             arcs[i][j]=0;  
  41.         }  
  42.     }  
  43.     cout<<"请输入边的信息:"<<endl;  
  44.     for (int i=0;i<arcnum;i++)  
  45.     {  
  46.         cin>>first>>Secend;  
  47.         //如果边有权值的话,则还应该输入权值  
  48.         int x = LocateVex(first);  
  49.         int y = LocateVex(Secend);  
  50.         arcs[x][y]=1;//如果是有权的话,这里应该是arc[x][y]=权值  
  51.     }  
  52. }   
  53. /* 
  54. 参数:v:表示顶点向量中一个值 
  55. 函数返回值:函数返回v在顶点向量中的下标 
  56. */  
  57. template<class VexType,class ArcType>  
  58. int MGraph<VexType,ArcType>::LocateVex(VexType v)  
  59. {  
  60.     for (int i=0;i<vexnum;i++)  
  61.     {  
  62.         if (vexs[i]==v)  
  63.         {  
  64.             return i;  
  65.         }  
  66.     }  
  67.     return -1;  
  68. }  
  69.   
  70. /* 
  71. 检查图中是不是有回向边 
  72. 思想: 
  73. 如果有回向边,则无环,反之有环 
  74. */  
  75. template<class VexType,class ArcType>  
  76. void MGraph<VexType,ArcType>::CheckCircle()  
  77. {  
  78.     int count=0;//环的个数  
  79.     int top=-1;  
  80.     int stack[MAX_Vertex_Num];  
  81.     bool inStack[MAX_Vertex_Num]={false};  
  82.     bool visited[MAX_Vertex_Num]={false};  
  83.     for (int i=0;i<vexnum;i++)  
  84.     {  
  85.         if (!visited[i])  
  86.         {  
  87.             DFS(i,visited,stack,top,inStack,count);  
  88.         }  
  89.     }  
  90. }  
  91.   
  92. template<class VexType,class ArcType>  
  93. void MGraph<VexType,ArcType>::DFS(int x,bool visited[MAX_Vertex_Num],int stack[MAX_Vertex_Num],int& top,bool inStack[MAX_Vertex_Num],int& count)  
  94. {  
  95.     visited[x]=true;  
  96.     stack[++top]=x;  
  97.     inStack[x]=true;  
  98.     for (int i=0;i<vexnum;i++)  
  99.     {  
  100.         if (arcs[x][i]!=0)//有边  
  101.         {  
  102.             if (!inStack[i])  
  103.             {  
  104.                 DFS(i,visited,stack,top,inStack,count);  
  105.             }  
  106.             else //条件成立,表示下标为x的顶点到 下标为i的顶点有环  
  107.             {  
  108.                 count++;  
  109.                 cout<<"第"<<count<<"环为:";  
  110.                 //从i到x是一个环,top的位置是x,下标为i的顶点在栈中的位置要寻找一下  
  111.                 //寻找起始顶点下标在栈中的位置  
  112.                 int t=0;  
  113.                 for (t=top;stack[t]!=i;t--);  
  114.                 //输出环中顶点  
  115.                 for (int j=t;j<=top;j++)  
  116.                 {  
  117.                     cout<<vexs[stack[j]];  
  118.                 }  
  119.                 cout<<endl;  
  120.             }  
  121.         }  
  122.     }  
  123.     //处理完结点后,退栈  
  124.     top--;  
  125.     inStack[x]=false;  
  126. }  
  127. int main()  
  128. {  
  129.     MGraph<char,int> G;  
  130.     G.CreateGraph();  
  131.     G.CheckCircle();  
  132.     system("pause");  
  133.     return 1;  
  134. }  

结果测试:

有向图:

 

结果:
 

原创粉丝点击