深度搜索的应用----有向图的连通性

有向图的连通性,首先看一下下面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-->2

2-->7

2-->8

8-->5

5-->3

3-->2

按照第二排输入的数据,就可以画出上面的有向图的连接状态

输出:

2, 3,5,8


方法一:

一个节点一个节点去判断是否形成环路:每次都是从第i个节点开始,例如:从节点i开始扫描,如果再次回到节点i,就说明形成了环路。

如果有10个节点就要扫描10次,如果有100个节点就要扫描100次,此种方法效率不是很高,但是容易理解。

扫描的方式我们采取深度搜索的方式。


具体代码如下:

  1. #include <iostream>
  2. using namespace std;
  3. bool success = false;
  4. bool isFinish = false;
  5. int visit[100];
  6. int AnswerN;
  7. int Answer[100];
  8. int N, M;
  9. int endNode;
  10. int A[100];
  11. int B[100];
  12. int array[100][100];
  13. void DFS(int array[100][100], int node)
  14. {
  15.     //当发生递归的时候,说明i节点已经被被访问,记录下它当时的访问状态,1代表该节点已经被访问过了
  16.     visit[node] = 1;
  17.     int i;
  18.     for (i = 0; i < N; ++i)
  19.     {
  20.         //如果该标志位为true表示已经深度搜索完成了,但是没有形成环路
  21.         isFinish = false;
  22.         //如果已经形成了环路,则不需要进行后续的深度搜索
  23.         if (success)
  24.         {
  25.             break;
  26.         }
  27.         
  28.         //如果第node节点和第i节点存在连通性
  29.         if (array[node][i] == 1)
  30.         {
  31.             //如果第i节点就是最终的节点,即找到了环路
  32.             if (i == endNode)
  33.             {
  34.                 success = true;
  35.                 //找到后,存储该点,并加1,因为程序下标0开始的,加1后对应题目中的节点标号
  36.                 Answer[AnswerN++] = i + 1;
  37.                 break;
  38.             }
  39.             // 如果不存在环路,但是还有剩余联通的节点,则继续进行深度搜索
  40.             if( visit[i] == 0)      
  41.             {
  42.                 DFS(array, i);
  43.                 //只把形成环路的点,存贮到最后的结果中,多余的点剔除在外
  44.                 if (isFinish == false)
  45.                 {
  46.                     Answer[AnswerN++] = i + 1;
  47.                 }
  48.             }
  49.         }
  50.         //第node节点,i从0循环到N都没有联通,则没有形成环路
  51.         isFinish = true;
  52.     }
  53.     
  54. }
  55. int main()
  56. {
  57.     int i, j;
  58.     cin >> N >> M;
  59.     for (i = 0; i < M; ++i)
  60.     {
  61.         cin >> A[i] >> B[i];
  62.     }
  63.     //初始化邻接矩阵和访问节点的标记位
  64.     for (i = 0; i < N; ++i)
  65.     {
  66.         for (j = 0; j < N; ++j)
  67.         {
  68.             array[i][j] = 0;
  69.         }
  70.         visit[i] = 0;
  71.         Answer[i] = 0;
  72.     }
  73.     //填入邻接矩阵的值
  74.     for (i = 0; i < M; ++i)
  75.     {
  76.         array[A[i] - 1][B[i] - 1] = 1;
  77.     }
  78.     AnswerN = 0;
  79.     success = false;
  80.     //对每个结点实施深度搜索
  81.     for (i = 0; i < N; ++i)
  82.     {
  83.         //每次对i节点进行深度搜索,都要初始化visit数组
  84.         for (j = 0; j < N; ++j)
  85.         {
  86.             visit[j] = 0;
  87.         }
  88.         //起点也即终点
  89.         endNode = i;
  90.         DFS(array, i);
  91.         //已经成环了,因为只要找到一条路径,所以不再寻找后续的环路
  92.         if (success)
  93.         {
  94.             break;
  95.         }    
  96.     }
  97.     if (success == false)
  98.     {
  99.         AnswerN = 0;
  100.     }
  101.     else
  102.     {
  103.         //如果存在环形队列则排序
  104.         for (i = 0; i < AnswerN; ++i)
  105.         {
  106.             for (j = i; j < AnswerN; ++j)
  107.             {
  108.                 if (Answer[i] > Answer[j])
  109.                 {
  110.                     int tmp = Answer[i];
  111.                     Answer[i] = Answer[j];
  112.                     Answer[j] = tmp;
  113.                 }
  114.             }
  115.         }
  116.     }
  117.     //输出最后的结果
  118.     if (AnswerN == 0)
  119.     {
  120.         cout << "0 " << endl;
  121.     }
  122.     else
  123.     {
  124.         for (i = 0; i < AnswerN; ++i)
  125.         {
  126.             cout << Answer[i] << "  ";
  127.         }
  128.         cout << endl;
  129.     }
  130.     return 0;
  131. }

方法二:

从第一个节点进行深度搜索,一次性走完该节点相关所有的深度搜索的节点。

如果存在环路,直接从找出来,如果不存在环路,是否有剩余未访问的节点,如果有再进行深度搜索的遍历。

相对来说该方法提高了效率,相当于深度搜索是每个联通的数据块,一个联通的数据块可能有环路,可能没有环路,如果没有环路,深度搜索下个联通的数据块。


具体代码如下:

  1. #include <iostream>
  2. using namespace std;
  3. bool success = false;
  4. int visit[100];
  5. int tree[100];
  6. int AnswerN;
  7. int Answer[100];
  8. int N, M;
  9. int A[100];
  10. int B[100];
  11. int array[100][100];
  12. int DFS(int array[100][100], int node)
  13. {    
  14.     visit[node] = 1;
  15.     int i;
  16.     int t;
  17.     for (i = 0; i < N; ++i)
  18.     {
  19.         //如果第node节点和第i节点存在连通性
  20.         if (array[node][i] == 1)
  21.         {
  22.             //如果找到了环路
  23.             if (visit[i] == 1)
  24.             {
  25.                 Answer[AnswerN++] = node;
  26.                 //找node节点的父节点
  27.                 int w = tree[node];
  28.                 //循环查找父节点,追溯至源头
  29.                 while (w != tree[w])
  30.                 {
  31.                     //父节点就是i节点,即此时形成了环路
  32.                     if (w == i)
  33.                     {
  34.                         Answer[AnswerN++] = w;
  35.                         return 1;
  36.                     }
  37.                     Answer[AnswerN++] = w;
  38.                     w = tree[w];
  39.                 }
  40.                 if (w == i)
  41.                 {
  42.                     Answer[AnswerN++] = w;
  43.                     return 1;
  44.                 }
  45.             }
  46.             else  //如果没有被访问过,则再进行深度搜索
  47.             {            
  48.                 //记录下i节点的父亲节点node,即node是i的父节点
  49.                 tree[i] = node;
  50.                 t = DFS(array, i);
  51.                 if (t == 1)
  52.                 {
  53.                     return 1;
  54.                 }
  55.             }
  56.         }
  57.     }        
  58.     return 0;
  59. }
  60. int main()
  61. {
  62.     int i, j;
  63.     cin >> N >> M;
  64.     for (i = 0; i < M; ++i)
  65.     {
  66.         cin >> A[i] >> B[i];
  67.     }
  68.     
  69.     for (i = 0; i < N; ++i)
  70.     {
  71.         for (j = 0; j < N; ++j)
  72.         {
  73.             array[i][j] = 0;
  74.         }
  75.         visit[i] = 0;
  76.         Answer[i] = 0;
  77.         //初始化父节点
  78.         tree[i] = i;
  79.     }
  80.     for (i = 0; i < M; ++i)
  81.     {
  82.         array[A[i] - 1][B[i] - 1] = 1;
  83.     }
  84.     AnswerN = 0;
  85.     for (i = 0; i < N; ++i)
  86.     {
  87.         //判断剩余的访问节点,如果没有被访问则再进行深度遍历,如果已经被访问了就不会访问该节点了
  88.         if (visit[i] == 0)
  89.         {
  90.             if (1 == DFS(array, i))
  91.             {
  92.                 success = true;
  93.                 break;
  94.             }
  95.             
  96.         }
  97.         success = false;
  98.     }
  99.     if (success == false)
  100.     {
  101.         AnswerN = 0;
  102.     }
  103.     else
  104.     {
  105.         //如果存在环形队列则排序
  106.         for (i = 0; i < AnswerN; ++i)
  107.         {
  108.             for (j = i; j < AnswerN; ++j)
  109.             {
  110.                 if (Answer[i] > Answer[j])
  111.                 {
  112.                     int tmp = Answer[i];
  113.                     Answer[i] = Answer[j];
  114.                     Answer[j] = tmp;
  115.                 }
  116.             }
  117.         }
  118.     }
  119.     if (AnswerN  == 0)
  120.     {
  121.         cout << "0" << endl;
  122.     }
  123.     else
  124.     {
  125.         for (i = 0; i < AnswerN; ++i)
  126.         {
  127.             cout << Answer[i]+1 << "  ";
  128.         }
  129.         cout << endl;
  130.     }
  131.     
  132.     return 0;
  133. }


最后给出几个参考用例

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值