圖的遍歷(搜索)算法(深度優先算法DFS和廣度優先算法BFS)


圖的遍歷的定義:
從圖的某個頂點出發訪問遍圖中所有頂點,且每個頂點僅被訪問一次。(連通圖與非連通圖)

深度優先遍歷(DFS);

1、訪問指定的起始頂點;

2、若當前訪問的頂點的鄰接頂點有未被訪問的,則任選一個訪問之;反之,退回到最近訪問過的頂點;直到與起始頂點相通的全部頂點都訪問完畢;

3、若此時圖中尚有頂點未被訪問,則再選其中一個頂點作為起始頂點並訪問之,轉 2; 反之,遍歷結束。

 連通圖的深度優先遍歷類似於樹的先根遍歷

如何判別V的鄰接點是否被訪問?

解決辦法:為每個頂點設立一個“訪問標志”。首先將圖中每個頂點的訪問標志設為 FALSE,  之后搜索圖中每個頂點,如果未被訪問,則以該頂點為起始點,進行深度

優先遍歷,否則繼續檢查下一頂點。

 
訪問指定的起始頂點;若當前訪問的頂點的鄰接頂點有未被訪問的,則任選一個訪問之;
反之,退回到最近訪問過的頂點;直到與起始頂點相通的全部頂點都訪問完畢;
 
回退到1,發現了新的沒有被訪問的結點
繼續回退,回退到0
再也找不到新的結點了,那么回退,回退到起始頂點,結束搜索

頂點的訪問序列為:    v0 , v1 , v4 , v5 , v6 , v2 , v3(不唯一)

實現過程:依靠棧,一維數組和圖的鄰接矩陣存儲方式

圖的鄰接矩陣存儲方式
 
使用一個一維數組存儲所有的頂點,對應的下標的元素為1(代表已經被訪問),0(代表沒有被訪問)
 
先訪問 v1,0進棧,0處置為1
 
繼續訪問 v2,1進棧,1處置為1
 
繼續訪問v4(依據鄰接矩陣),3入棧,3處置為1
 
繼續訪問 v8,7入棧,7處置為1
 
繼續訪問 v5,4入棧,4處置為1
 
繼續訪問,發現沒有還沒訪問的結點了,那么好,退棧(也就是回退)開始,回退到 v1處,也就是0的時候,發現了沒有被訪問的結點,那么繼續訪問之

繼續訪問 v3,2進棧,2處置為1,繼續訪問v6,5進棧,5處置為1,繼續訪問v7,6進棧,6處置為1
 
發現沒有還沒被訪問的結點了,那么好,繼續回退(也就是退棧的過程)
 
 
一直到棧空,說明深度優先搜索完畢。結束程序。

遍歷圖的過程實質上是對每個頂點查找其鄰接點的過程,所耗費的時間取決於所采用的存儲結構。
對圖中的每個頂點至多調用1次DFS算法,因為一旦某個頂點已訪問過,則不再從它出發進行搜索。

鄰接鏈表表示:查找每個頂點的鄰接點所需時間為O(e),e為邊(弧)數,算法時間復雜度為O(n+e)

數組表示:查找每個頂點的鄰接點所需時間為O(n2),n為頂點數,算法時間復雜度為O(n2)

代碼如下

//訪問標志數組
int visited[MAX] = {0};

//用鄰接表方式實現深度優先搜索(遞歸方式)
//v 傳入的是第一個需要訪問的頂點
void DFS(MGraph G, int v)
{
    //圖的頂點的搜索指針
    ArcNode *p;
    //置已訪問標記
    visited[v] = 1;
    //輸出被訪問頂點的編號
    printf("%d  ", v);
    //p指向頂點v的第一條弧的弧頭結點
    p = G.vertices[v].firstarc;
    while (p != NULL)
    {
        //若p->adjvex頂點未訪問,遞歸訪問它
        if (visited[p->adjvex] == 0)
        {
            DFS(G, p->adjvex);
        }
        //p指向頂點v的下一條弧的弧頭結點
        p = p->nextarc;
    }
}

廣度優先搜索(BFS)

方法:從圖的某一結點出發,首先依次訪問該結點的所有鄰接頂點 Vi1, Vi2, …, Vin 再按這些頂點被訪問的先后次序依次訪問與它們相鄰接的所有未被訪問的頂點,重復此過程,直至所有頂點均被訪問為止。

頂點的訪問次序

實現過程:依靠隊列和一維數組來實現

 1 #include <iostream>
 2 #include<queue>
 3 using namespace std;
 4 
 5 const int MAX = 10;
 6 //輔助隊列的初始化,置空的輔助隊列Q,類似二叉樹的層序遍歷過程
 7 queue<int> q;
 8 //訪問標記數組
 9 bool visited[MAX];
10 //圖的廣度優先搜索算法
11 void BFSTraverse(Graph G, void (*visit)(int v))
12 {
13     int v = 0;
14     //初始化訪問標記的數組
15     for (v = 0; v < G.vexnum; v++)
16     {
17         visited[v] = false;
18     }
19     //依次遍歷整個圖的結點
20     for (v = 0; v < G.vexnum; v++)
21     {
22         //如果v尚未訪問,則訪問 v
23         if  (!visited[v])
24         {
25             //把 v 頂點對應的數組下標處的元素置為真,代表已經訪問了
26             visited[v] = true;
27             //然后v入隊列,利用了隊列的先進先出的性質
28             q.push(v);
29             //訪問 v,打印處理
30             cout << q.back() << " ";
31             //隊不為空時
32             while (!q.empty())
33             {
34                 //隊頭元素出隊,並把這個出隊的元素置為 u,類似層序遍歷
35                 Graph *u = q.front();
36                 q.pop();
37                 //w為u的鄰接頂點
38                 for (int w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G,u,w))
39                 {
40                     //w為u的尚未訪問的鄰接頂點
41                     if (!visited[w])
42                     {
43                         visited[w] = true;
44                         //然后 w 入隊列,利用了隊列的先進先出的性質
45                         q.push(w);
46                         //訪問 w,打印處理
47                         cout << q.back() << " ";
48                     }//end of if
49                 }//end of for
50             }//end of while
51         }//end of if
52     }// end of for
53 }

 

歡迎關注

 

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM