1. 程式人生 > >深度搜索的應用----有向圖的連通性

深度搜索的應用----有向圖的連通性

有向圖的連通性,首先看一下下面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.         //如果第node節點和第i節點存在連通性
  28.         if (array[node][i] == 1)
  29.         {
  30.             //如果第i節點就是最終的節點,即找到了環路
  31.             if (i == endNode)
  32.             {
  33.                 success = true;
  34.                 //找到後,儲存該點,並加1,因為程式下標0開始的,加1後對應題目中的節點標號
  35.                 Answer[AnswerN++] = i + 1;
  36.                 break;
  37.             }
  38.             // 如果不存在環路,但是還有剩餘聯通的節點,則繼續進行深度搜索
  39.             if( visit[i] == 0)      
  40.             {
  41.                 DFS(array, i);
  42.                 //只把形成環路的點,存貯到最後的結果中,多餘的點剔除在外
  43.                 if (isFinish == false)
  44.                 {
  45.                     Answer[AnswerN++] = i + 1;
  46.                 }
  47.             }
  48.         }
  49.         //第node節點,i從0迴圈到N都沒有聯通,則沒有形成環路
  50.         isFinish = true;
  51.     }
  52. }
  53. int main()
  54. {
  55.     int i, j;
  56.     cin >> N >> M;
  57.     for (i = 0; i < M; ++i)
  58.     {
  59.         cin >> A[i] >> B[i];
  60.     }
  61.     //初始化鄰接矩陣和訪問節點的標記位
  62.     for (i = 0; i < N; ++i)
  63.     {
  64.         for (j = 0; j < N; ++j)
  65.         {
  66.             array[i][j] = 0;
  67.         }
  68.         visit[i] = 0;
  69.         Answer[i] = 0;
  70.     }
  71.     //填入鄰接矩陣的值
  72.     for (i = 0; i < M; ++i)
  73.     {
  74.         array[A[i] - 1][B[i] - 1] = 1;
  75.     }
  76.     AnswerN = 0;
  77.     success = false;
  78.     //對每個結點實施深度搜索
  79.     for (i = 0; i < N; ++i)
  80.     {
  81.         //每次對i節點進行深度搜索,都要初始化visit陣列
  82.         for (j = 0; j < N; ++j)
  83.         {
  84.             visit[j] = 0;
  85.         }
  86.         //起點也即終點
  87.         endNode = i;
  88.         DFS(array, i);
  89.         //已經成環了,因為只要找到一條路徑,所以不再尋找後續的環路
  90.         if (success)
  91.         {
  92.             break;
  93.         }    
  94.     }
  95.     if (success == false)
  96.     {
  97.         AnswerN = 0;
  98.     }
  99.     else
  100.     {
  101.         //如果存在環形佇列則排序
  102.         for (i = 0; i < AnswerN; ++i)
  103.         {
  104.             for (j = i; j < AnswerN; ++j)
  105.             {
  106.                 if (Answer[i] > Answer[j])
  107.                 {
  108.                     int tmp = Answer[i];
  109.                     Answer[i] = Answer[j];
  110.                     Answer[j] = tmp;
  111.                 }
  112.             }
  113.         }
  114.     }
  115.     //輸出最後的結果
  116.     if (AnswerN == 0)
  117.     {
  118.         cout << "0 " << endl;
  119.     }
  120.     else
  121.     {
  122.         for (i = 0; i < AnswerN; ++i)
  123.         {
  124.             cout << Answer[i] << "  ";
  125.         }
  126.         cout << endl;
  127.     }
  128.     return 0;
  129. }

方法二:

從第一個節點進行深度搜索,一次性走完該節點相關所有的深度搜索的節點。

如果存在環路,直接從找出來,如果不存在環路,是否有剩餘未訪問的節點,如果有再進行深度搜索的遍歷。

相對來說該方法提高了效率,相當於深度搜索是每個聯通的資料塊,一個聯通的資料塊可能有環路,可能沒有環路,如果沒有環路,深度搜索下個聯通的資料塊。

具體程式碼如下:

  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.     for (i = 0; i < N; ++i)
  69.     {
  70.         for (j = 0; j < N; ++j)
  71.         {
  72.             array[i][j] = 0;
  73.         }
  74.         visit[i] = 0;
  75.         Answer[i] = 0;
  76.         //初始化父節點
  77.         tree[i] = i;
  78.     }
  79.     for (i = 0; i < M; ++i)
  80.     {
  81.         array[A[i] - 1][B[i] - 1] = 1;
  82.     }
  83.     AnswerN = 0;
  84.     for (i = 0; i < N; ++i)
  85.     {
  86.         //判斷剩餘的訪問節點,如果沒有被訪問則再進行深度遍歷,如果已經被訪問了就不會訪問該節點了
  87.         if (visit[i] == 0)
  88.         {
  89.             if (1 == DFS(array, i))
  90.             {
  91.                 success = true;
  92.                 break;
  93.             }
  94.         }
  95.         success = false;
  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.     if (AnswerN  == 0)
  118.     {
  119.         cout << "0" << endl;
  120.     }
  121.     else
  122.     {
  123.         for (i = 0; i < AnswerN; ++i)
  124.         {
  125.             cout << Answer[i]+1 << "  ";
  126.         }
  127.         cout << endl;
  128.     }
  129.     return 0;
  130. }

最後給出幾個參考用例

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