圖的遍歷(搜索)算法(深度優先算法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 再按這些頂點被訪問的先後次序依次訪問與它們相鄰接的所有未被訪問的頂點,重複此過程,直至所有頂點均被訪問爲止。
頂點的訪問次序
實現過程:依靠隊列和一維數組來實現
#include <iostream>
#include<queue>
usingnamespace std;
const int MAX = 10;
//輔助隊列的初始化,置空的輔助隊列Q,類似二叉樹的層序遍歷過程
queue <int> q;
//訪問標記數組
bool visited[MAX];
//圖的廣度優先搜索算法
void BFSTraverse(Graph G, void (*visit)(int v))
{
//初始化訪問標記的數組
for (v = 0; v < G.vexnum; v++)
{
visited[v] = false;
}
//依次遍歷整個圖的結點
for (v = 0; v < G.vexnum; v++)
{
//如果v尚未訪問,則訪問 v
if (!visited[v])
{
//把 v 頂點對應的數組下標處的元素置爲真,代表已經訪問了
visited[v] = true;
//然後v入隊列,利用了隊列的先進先出的性質
q.push(v);
//訪問 v,打印處理
cout << q.back() << "";
//隊不爲空時
while (!q.empty())
{
//隊頭元素出隊,並把這個出隊的元素置爲 u,類似層序遍歷
Graph *u = q.front();
q.pop();
//w爲u的鄰接頂點
for (int w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G,u,w))
{
//w爲u的尚未訪問的鄰接頂點
if (!visited[w])
{
visited[w] = true;
//然後 w 入隊列,利用了隊列的先進先出的性質
q.push(w);
//訪問 w,打印處理
cout << q.back() << "";
}//end of if
}//end of for
}//end of while
}//end of if
}// end of for
}