有向图的连通性,首先看一下下面2个图,
在图1 中A->B-C->A,那么我们就说这个有向图存在环路。
在图2中A->B->C, A->C,无法形成一个环路,则称A,B,C三点不存在环路
图1 图2
以下的例子是我自己临时起意乱画的,也不知道合适与否,有兴趣的朋友讲究看一下吧。^_^
从上面的图可以明显的看出,有向图形成了一个环路,2-->8-->5-->3-->2(节点6作为一个单独的节点没有和任何节点有联系)
题目:判断一个有向图中是否存在环路,如果存在环路,打印环路中的一条通路的节点号并按升序排列,如果存在多条环路,只要打印其中一条环路即可。
例如上图就打印:2,3,5,8
输入:
8 7 //8代表节点数目,7代表有向图的联通路径总数
1 4 4 2 2 7 2 8 8 5 5 3 3 2 //这组输入数据代表有向图的方向和连通性
1-->4
4-->22-->7
2-->8
8-->5
5-->3
3-->2
按照第二排输入的数据,就可以画出上面的有向图的连接状态
输出:
2, 3,5,8
方法一:
一个节点一个节点去判断是否形成环路:每次都是从第i个节点开始,例如:从节点i开始扫描,如果再次回到节点i,就说明形成了环路。
如果有10个节点就要扫描10次,如果有100个节点就要扫描100次,此种方法效率不是很高,但是容易理解。
扫描的方式我们采取深度搜索的方式。
具体代码如下:
- #include <iostream>
- using namespace std;
- bool success = false;
- bool isFinish = false;
- int visit[100];
- int AnswerN;
- int Answer[100];
- int N, M;
- int endNode;
- int A[100];
- int B[100];
- int array[100][100];
- void DFS(int array[100][100], int node)
- {
- //当发生递归的时候,说明i节点已经被被访问,记录下它当时的访问状态,1代表该节点已经被访问过了
- visit[node] = 1;
- int i;
- for (i = 0; i < N; ++i)
- {
- //如果该标志位为true表示已经深度搜索完成了,但是没有形成环路
- isFinish = false;
- //如果已经形成了环路,则不需要进行后续的深度搜索
- if (success)
- {
- break;
- }
- //如果第node节点和第i节点存在连通性
- if (array[node][i] == 1)
- {
- //如果第i节点就是最终的节点,即找到了环路
- if (i == endNode)
- {
- success = true;
- //找到后,存储该点,并加1,因为程序下标0开始的,加1后对应题目中的节点标号
- Answer[AnswerN++] = i + 1;
- break;
- }
- // 如果不存在环路,但是还有剩余联通的节点,则继续进行深度搜索
- if( visit[i] == 0)
- {
- DFS(array, i);
- //只把形成环路的点,存贮到最后的结果中,多余的点剔除在外
- if (isFinish == false)
- {
- Answer[AnswerN++] = i + 1;
- }
- }
- }
- //第node节点,i从0循环到N都没有联通,则没有形成环路
- isFinish = true;
- }
- }
- int main()
- {
- int i, j;
- cin >> N >> M;
- for (i = 0; i < M; ++i)
- {
- cin >> A[i] >> B[i];
- }
- //初始化邻接矩阵和访问节点的标记位
- for (i = 0; i < N; ++i)
- {
- for (j = 0; j < N; ++j)
- {
- array[i][j] = 0;
- }
- visit[i] = 0;
- Answer[i] = 0;
- }
- //填入邻接矩阵的值
- for (i = 0; i < M; ++i)
- {
- array[A[i] - 1][B[i] - 1] = 1;
- }
- AnswerN = 0;
- success = false;
- //对每个结点实施深度搜索
- for (i = 0; i < N; ++i)
- {
- //每次对i节点进行深度搜索,都要初始化visit数组
- for (j = 0; j < N; ++j)
- {
- visit[j] = 0;
- }
- //起点也即终点
- endNode = i;
- DFS(array, i);
- //已经成环了,因为只要找到一条路径,所以不再寻找后续的环路
- if (success)
- {
- break;
- }
- }
- if (success == false)
- {
- AnswerN = 0;
- }
- else
- {
- //如果存在环形队列则排序
- for (i = 0; i < AnswerN; ++i)
- {
- for (j = i; j < AnswerN; ++j)
- {
- if (Answer[i] > Answer[j])
- {
- int tmp = Answer[i];
- Answer[i] = Answer[j];
- Answer[j] = tmp;
- }
- }
- }
- }
- //输出最后的结果
- if (AnswerN == 0)
- {
- cout << "0 " << endl;
- }
- else
- {
- for (i = 0; i < AnswerN; ++i)
- {
- cout << Answer[i] << " ";
- }
- cout << endl;
- }
- return 0;
- }
方法二:
从第一个节点进行深度搜索,一次性走完该节点相关所有的深度搜索的节点。如果存在环路,直接从找出来,如果不存在环路,是否有剩余未访问的节点,如果有再进行深度搜索的遍历。
相对来说该方法提高了效率,相当于深度搜索是每个联通的数据块,一个联通的数据块可能有环路,可能没有环路,如果没有环路,深度搜索下个联通的数据块。
具体代码如下:
- #include <iostream>
- using namespace std;
- bool success = false;
- int visit[100];
- int tree[100];
- int AnswerN;
- int Answer[100];
- int N, M;
- int A[100];
- int B[100];
- int array[100][100];
- int DFS(int array[100][100], int node)
- {
- visit[node] = 1;
- int i;
- int t;
- for (i = 0; i < N; ++i)
- {
- //如果第node节点和第i节点存在连通性
- if (array[node][i] == 1)
- {
- //如果找到了环路
- if (visit[i] == 1)
- {
- Answer[AnswerN++] = node;
- //找node节点的父节点
- int w = tree[node];
- //循环查找父节点,追溯至源头
- while (w != tree[w])
- {
- //父节点就是i节点,即此时形成了环路
- if (w == i)
- {
- Answer[AnswerN++] = w;
- return 1;
- }
- Answer[AnswerN++] = w;
- w = tree[w];
- }
- if (w == i)
- {
- Answer[AnswerN++] = w;
- return 1;
- }
- }
- else //如果没有被访问过,则再进行深度搜索
- {
- //记录下i节点的父亲节点node,即node是i的父节点
- tree[i] = node;
- t = DFS(array, i);
- if (t == 1)
- {
- return 1;
- }
- }
- }
- }
- return 0;
- }
- int main()
- {
- int i, j;
- cin >> N >> M;
- for (i = 0; i < M; ++i)
- {
- cin >> A[i] >> B[i];
- }
- for (i = 0; i < N; ++i)
- {
- for (j = 0; j < N; ++j)
- {
- array[i][j] = 0;
- }
- visit[i] = 0;
- Answer[i] = 0;
- //初始化父节点
- tree[i] = i;
- }
- for (i = 0; i < M; ++i)
- {
- array[A[i] - 1][B[i] - 1] = 1;
- }
- AnswerN = 0;
- for (i = 0; i < N; ++i)
- {
- //判断剩余的访问节点,如果没有被访问则再进行深度遍历,如果已经被访问了就不会访问该节点了
- if (visit[i] == 0)
- {
- if (1 == DFS(array, i))
- {
- success = true;
- break;
- }
- }
- success = false;
- }
- if (success == false)
- {
- AnswerN = 0;
- }
- else
- {
- //如果存在环形队列则排序
- for (i = 0; i < AnswerN; ++i)
- {
- for (j = i; j < AnswerN; ++j)
- {
- if (Answer[i] > Answer[j])
- {
- int tmp = Answer[i];
- Answer[i] = Answer[j];
- Answer[j] = tmp;
- }
- }
- }
- }
- if (AnswerN == 0)
- {
- cout << "0" << endl;
- }
- else
- {
- for (i = 0; i < AnswerN; ++i)
- {
- cout << Answer[i]+1 << " ";
- }
- cout << endl;
- }
- return 0;
- }
最后给出几个参考用例
5 5
4 3 2 4 3 5 3 2 1 4 //输出 2 3 4
5 5
4 3 2 4 3 5 2 3 1 4 //输出0
6 5
1 5 6 4 3 1 5 3 4 6 //输出 1 3 5