1. 程式人生 > >資料結構與演算法18-圖的遍歷

資料結構與演算法18-圖的遍歷

從圖中某一個頂點出發訪遍圖中其餘頂點,且使每個頂點權被訪問一次,這一過程就叫做圖的遍歷(Traversing   Graph)。

深度優先遍歷(Depth_First_Search)

從圖中某個頂點v出發,訪問此頂點,然後從v的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中所有和v有路徑想通的頂點都被訪問到

事實上,我們這裡講到的是連通圖,對於非連通圖,只需要對它的連通分量分別進行深度優先遍歷,即在前一個頂點進行一次深度優先遍歷後,若圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點作起始點,重複上述過程,直至圖中所有頂點都補充訪問到為止

如果我們用的是鄰接矩陣方式,則程式碼如下:

typedef  int  Boolean;

Boolean  visited[MAX];

/*鄰接矩陣的深度優先遞迴演算法*/

void   DFS(MGraph  G,int i)

{

       int   j;

       visited[i] = TRUE;

       printf(“%c”,G.vexs[i]);

       for(j=0;j<G.numVertexes;j++)

            if(G.arc[i][j]==1 && !visited[j]) DFS(G,j);  //對訪問的鄰接頂點遞迴呼叫。

}



/*鄰接矩陣的深度遍歷操作*/

void  DFSTravese(MGraph   G)

{

    int  i;

    for(i=0;i<G.numVertexes;i++)

          visited[i]=FALSE;  //初始化訪問狀態為未訪問

     for(i=0;i<G.numVertexes;i++)

          if(!visited[!])

                DFS(G,i);

}

如果結構圖是鄰接表結構,其DFSTraverse函式的程式碼是幾乎相同的,只是在遞迴函式中因為將陣列換成連結串列而不同

程式碼如下:

/*鄰接連結串列的深度優先遞迴演算法*/

void  DFS(GrapAdjList  GL,int  i)

{

        EdgeNode   *p;

         visited[i] = true;

         printf(“%c”,GL->adjList[i].data); //列印頂點

         p=GL->adjList[i].firstedge;

         while(p)

         {

                if(!visited[p->adjvex])

                      DFS(GL,p-adjvex);

                 p=p->next;

         }

}



/*鄰接表的深度遍歷操作*/

void  DFSTraverse(GraphAdjList   GL)

{

       int  i;

      for(i=0;i<GL->numVertexes;i++)

                    visited[i] = FALSE;

          for(i=0;i<GL->numVertexes;i++)

           if(!visited[i])

                 DFS(GL,i);

}

對比丙從此不同儲存結構的深度優先遍歷演算法,對於n個頂點e條邊的圖來說,鄰接矩陣由於是二維陣列,要查詢每個頂點的鄰接點需要訪問矩陣中的怕有元素,因此需要O(n2)的時間。而鄰接表做的儲存結構時,找鄰接點的所需的時間取決於頂點和邊的數量,所以是O(n+e)顯然對於點多邊少的稀疏圖來說,鄰接表結構使得演算法在時間效率上大大提高。

對於有向圖而非言,由於它只是對通道存在可行或不可行,演算法上沒有變化,是完全可以通用的。

廣度優先遍歷

又稱為廣度優先搜尋,簡稱BFS。

如果說圖的深度優先遍歷類似樹的前序遍歷,那麼圖的廣度優先遍歷類似於樹的層序遍歷。

如圖的圖稍微變形,變形原則是頂點A放置在最上第一層,讓與它有邊的頂點B、F為第二層,再讓與BF有邊的頂點C、I、G、E為第三層,再將這四個頂點的邊的D、H放在第四層

鄰接矩陣的廣度優先遍歷演算法

void    SFSTraverse(MGraph    G)

{

         int   i,j;

         Queue   Q;

         for(i=0;i<G.numVertexes;i++)

                visited[i] = FALSE;

         InitQueue(&Q);

         for(i=0;i<G.numVertexes;i++) //對第一個頂點做迴圈

         {

               if(!visited[i])

                {

                       visited[i] = TRUE;

                       printf(“%c”,G.vexs[i]);    //列印頂點,也可以其他操作

                       EnQueue(&Q,&i);                   //將此頂點入佇列

                        while(!QueueEmpty(Q))

                        {

                             //佇列不為空

                             DeQueue(&Q,&i);   //待隊中元素出隊,賦值給i

                            for(j=0;j<G.numVertexes;j++)

                           {   //判斷其它頂點和當前頂點存在邊且未訪問過

                                   if(G.arc[i][j]==1 &&!visited[j])

                                     {

                                            visited[j] = TRUE;

                                             printf(“%c”,G.vexs[j]);

                                             EnQueue(&Q,j);    //將找到的此頂點入佇列

                                          }

                             }

                         }

                   }

}

鄰接表的廣度遍歷演算法

void  BFSTraverse(GraphAdjList    GL)

{

          int   i;

          EdgeNode   *p;

          Queue    Q;

          for(i=0;i<GL->numVertexes;i++)

                visited[i] = FALSE;

           InitQueue(&Q);

          for(i=0;i<GL->numVertexes;i++)

          {

                   if(!visited[i])

                     {

                           visited[i]=TRUE;

                            printf(“%c”,GL->adjList[i].data);

                            EnQueue(&Q,i);

                            while(!QueueEmpty(Q))

                            {

                                 DeQueue(&Q,&i);

                                p=GL->adjList[i].firstedge; //找到當前頂點邊錶鏈表頭指標

                                 while(p)

                                  {

                                          if(!visited[p->adjvex])

                                          {

                                              visited[p->adjvex] = TRUE;

                                              printf(“%c”,GL->adjList[p->adjvex].data);

                                               EnQueue(&Q,p->adjvex);

                                           }

                                            p =p->next;

                                   }

                            }

                      }

           }

}

對比的圖的深度優先遍歷與廣度優先遍歷演算法,你會發現,它們在時間複雜度上是一樣的,不同之處僅僅在對於頂點訪問的順序不同。可見兩者在全圖遍歷上是沒有優劣之分的,只是視不同的情況選擇不同的演算法。