資料結構:圖的遍歷--深度優先、廣度優先
阿新 • • 發佈:2019-01-04
圖的遍歷:深度優先、廣度優先
遍歷
圖的遍歷是指從圖中的某一頂點出發,按照一定的策略訪問圖中的每一個頂點。當然,每個頂點有且只能被訪問一次。
在圖的遍歷中,深度優先和廣度優先是最常使用的兩種遍歷方式。這兩種遍歷方式對無向圖和有向圖都是適用的,並且都是從指定的頂點開始遍歷的。先看下兩種遍歷方式的遍歷規則:
深度優先
深度優先遍歷也叫深度優先搜尋(Depth First Search)。它的遍歷規則:不斷地沿著頂點的深度方向遍歷。頂點的深度方向是指它的鄰接點方向。
具體點,給定一圖G=<V,E>,用visited[i]表示頂點i的訪問情況,則初始情況下所有的visited[i]都為false。假設從頂點V
所謂的第一個是指在某種儲存結構中(鄰接矩陣、鄰接表),所有鄰接點中儲存位置最近的,通常指的是下標最小的。在遍歷的過程中有兩種情況經常出現
- 某個頂點的鄰接點都已被訪問過的情況,此時需回溯已訪問過的頂點。
- 圖不連通,所有的已訪問過的頂點都已回溯完了,仍找不出未被訪問的頂點。此時需從下標0開始檢測visited[i],找到未被訪問的頂點i,從i開始新一輪的深度搜索。
從V0開始遍歷 遍歷分析:V0有兩個鄰接點V1和V2,選擇下標最小的V1遍歷。接著從V1開始深度遍歷,
V3->V0->V1->V2->V4。
V4->V2->V0->V1->V3。
以上結果,我們稍後用於測試程式。 結合在圖的實現:鄰接矩陣中的程式碼,我們看下在鄰接矩陣形式下的圖的深度遍歷演算法:
深度優先程式碼
/*
深度優先搜尋
從vertex開始遍歷,visit是遍歷頂點的函式指標
*/
void Graph::dfs(int vertex, void (*visit)(int))
{
stack<int> s;
//visited[i]用於標記頂點i是否被訪問過
bool *visited = new bool[numV];
//count用於統計已遍歷過的頂點數
int i, count;
for (i = 0; i < numV; i++)
visited[i] = false;
count = 0;
while (count < numV)
{
visit(vertex);
visited[vertex] = true;
s.push(vertex);
count++;
if (count == numV)
break;
while (visited[vertex])
{
for (i = 0; i < numV
&& (visited[i]
|| matrix[vertex][i] == 0 || matrix[vertex][i] == MAXWEIGHT); i++);
if (i == numV) //當前頂點vertex的所有鄰接點都已訪問完了
{
if (!s.empty())
{
s.pop(); //此時vertex正是棧頂,應先出棧
if (!s.empty())
{
vertex = s.top();
s.pop();
}
else //若棧已空,則需從頭開始尋找新的、未訪問過的頂點
{
for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
}
}
else //若棧已空,則需從頭開始尋找新的、未訪問過的頂點
{
for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
}
}
else //找到新的頂點應更新當前訪問的頂點vertex
vertex = i;
}
}
delete[]visited;
}
其它程式碼前面已經見過,就不給出了,下面看下圖的廣度遍歷。深度遍歷和廣度遍歷的測試,稍後一併給出。
廣度優先
廣度優先遍歷也叫廣度優先搜尋(Breadth First Search)。它的遍歷規則:- 先訪問完當前頂點的所有鄰接點。(應該看得出廣度的意思)
- 先訪問頂點的鄰接點先於後訪問頂點的鄰接點被訪問。
從V0開始遍歷 遍歷分析:V0有兩個鄰接點V1和V2,於是按序遍歷V1、V2。V1先於V2被訪問,於是V1的鄰接點應先於V2的鄰接點被訪問,那就是接著訪問V3。V2無鄰接點,只能看V3的鄰接點了,而V0已被訪問過了。此時需檢測visited[i],只有V4了。廣度遍歷完畢。
遍歷序列是
V0->V1->V2->V3->V4。 從其它頂點出發的廣度優先遍歷序列是 V1->V3->V0->V2->V4。 V2->V0->V1->V3->V4。
V3->V0->V1->V2->V4。
V4->V2->V0->V1->V3。
以上結果,我們同樣用於測試程式。
在鄰接矩陣下,圖的廣度遍歷演算法
廣度優先程式碼
/*
廣度優先搜尋
從vertex開始遍歷,visit是遍歷頂點的函式指標
*/
void Graph::bfs(int vertex, void(*visit)(int))
{
//使用佇列
queue<int> q;
//visited[i]用於標記頂點i是否被訪問過
bool *visited = new bool[numV];
//count用於統計已遍歷過的頂點數
int i, count;
for (i = 0; i < numV; i++)
visited[i] = false;
q.push(vertex);
visit(vertex);
visited[vertex] = true;
count = 1;
while (count < numV)
{
if (!q.empty())
{
vertex = q.front();
q.pop();
}
else
{
for (vertex = 0; vertex < numV && visited[vertex]; vertex++);
visit(vertex);
visited[vertex] = true;
count++;
if (count == numV)
return;
q.push(vertex);
}
//程式碼走到這裡,vertex是已經訪問過的頂點
for (int i = 0; i < numV; i++)
{
if (!visited[i] && matrix[vertex][i] > 0 && matrix[vertex][i] < MAXWEIGHT)
{
visit(i);
visited[i] = true;
count ++;
if (count == numV)
return;
q.push(i);
}
}
}
delete[]visited;
}
結合兩種遍歷的程式碼,我們對同一幅圖進行測試,它的主函式是
void visit(int vertex)
{
cout << setw(4) << vertex;
}
int main()
{
cout << "******圖的遍歷:深度優先、廣度優先***by David***" << endl;
bool isDirected, isWeighted;
int numV;
cout << "建圖" << endl;
cout << "輸入頂點數 ";
cin >> numV;
cout << "邊是否帶權值,0(不帶) or 1(帶) ";
cin >> isWeighted;
cout << "是否是有向圖,0(無向) or 1(有向) ";
cin >> isDirected;
Graph graph(numV, isWeighted, isDirected);
cout << "這是一個";
isDirected ? cout << "有向、" : cout << "無向、";
isWeighted ? cout << "有權圖" << endl : cout << "無權圖" << endl;
graph.createGraph();
cout << "列印鄰接矩陣" << endl;
graph.printAdjacentMatrix();
cout << endl;
cout << "深度遍歷" << endl;
for (int i = 0; i < numV; i++)
{
graph.dfs(i, visit);
cout << endl;
}
cout << endl;
cout << "廣度遍歷" << endl;
for (int i = 0; i < numV; i++)
{
graph.bfs(i, visit);
cout << endl;
}
system("pause");
return 0;
}
執行
仔細對照測試結果,我們的程式碼是沒有問題的。
小結
對於某個圖來說,深度優先遍歷和廣度優先遍歷的序列不是唯一的,但當圖的儲存結構一確定,它的遍歷序列就是唯一的。因為當有多個候選點時,我們總是優先選擇下標最小的。若有所幫助,頂一個哦!
專欄目錄: