【0】README
0.1) 本文總結於 數據結構與算法分析, 源代碼均為原創, 旨在 理解 “DFS應用——遍歷有向圖+判斷有向圖是否有圈” 的idea 並用源代碼加以實現 ;
0.2) 判斷有向圖是否有圈的rule—— 一個有向圖是無圈圖當且僅當它沒有背向邊,背向邊定義,參見: http://blog.csdn.net/pacosonswjtu/article/details/49967255
0.3) ** 代碼最后還添加了打印dfs遍歷路徑所產生的集合, 對的,dfs 就應該這么玩**,Bingo!
【1】有向圖相關
1.1)對有向圖進行DFS的idea:利用與無向圖相同的思路, 也可以通過深度優先搜索以線性時間遍歷有向圖。如果圖不是強連通的,那么從某個節點開始的DFS可能訪問不了所有的節點。在這種情況下, 我們在某個未作標記的節點處開始,反復執行DFS, 直到所有節點都被訪問到;
1.2)基於以上描述, 我們看個荔枝(這只是一種可能的case):
- step1)從頂點B 任意開始深度優先搜索, 訪問頂點B, C, A, D, E;
- step2)從頂點 F 任意開始DFS, 訪問頂點 F;
- step3)從頂點 H 任意開始DFS, 訪問頂點 H, J, I;
- step4)從頂點 G 任意開始DFS, 訪問頂點 G;
1.3)對於以上的DFS過程, 對應的搜索優先搜索樹如下圖所示:
對上圖的分析(Analysis):
- A1)深度優先生成森林中虛線箭頭是一些(v, w)邊, 其中的w 在考察時已經做了標記;
- A2)我們看到,存在三種類型的邊並不通向新頂點:
- A2.1)背向邊:如(A,B) 和 (I,H);
- A2.2)前向邊:如(C,D) 和 (C,E), 它們從樹的一個節點通向一個后裔;
- A2.3)交叉邊:如(F,C)和(G,F), 它們把不直接相關的兩個樹節點連接起來;
- A3)深度優先搜索森林一般通過吧一些子節點和一些新的樹從左到右添加到森林中形成。 在以這種方式構成的有向圖的深度優先搜索中,交叉邊總是從右到左進行的;
1.4)深度優先搜索(DFS)的一個用途是: 檢測一個有向圖是否是無圈圖;
- 4.1) 法則如下: 一個有向圖是無圈圖當且僅當它沒有背向邊;(上面的圖有背向邊, 因此它不是無圈圖)
- 4.2)拓撲排序也可以用來確定一個圖是否是無圈圖。進行拓撲排序的另一種方法是通過深度優先生成森林的后序遍歷給頂點指定拓撲編號N, N-1, ..., 1; 只要圖是無圈的,這種排序就是一致的;
【2】source code + printing results(此處的dfs是對原始dfs修改而成的,與原始的dfs不同,但idea一樣)
2.1)download source code: https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p248_dfs_directed_graph
2.2)source code at a glance:(for complete code , please click the given link above)
void dfs(Vertex vertex, int depth)
{
int i;
int visitFlag;
AdjTable temp;
Vertex adjVertex;
//printf("\n\t visited[%c] = 1 ", flag[vertex]);
visited[vertex] = 1; // update visited status of vertex
vertexIndex[vertex] = counter++; // number the vertex with counter
temp = adj[vertex];
visitFlag = 0;
while(temp->next)
{
adjVertex = temp->next->vertex;
if(visited[adjVertex]) // judge whether the adjVertes was visited before
{
if(vertexIndex[vertex] > vertexIndex[adjVertex] && parent[vertex] != adjVertex)
{
parent[adjVertex] = vertex; // building back side, attention of condition of building back side above
// just for printing effect
for(i = 0; i < depth; i++)
printf(" ");
printf(" v[%c]->v[%c] (backside) \n", flag[vertex], flag[adjVertex]);
}
}
//if(!visited[adjVertex])
else
{
if(vertex == start)
visitFlag = 1;
parent[adjVertex] = vertex;
// just for printing effect
for(i = 0; i < depth; i++)
printf(" ");
printf(" v[%c]->v[%c] (building edge) \n", flag[vertex], flag[adjVertex]);
dfs(adjVertex, depth+1);
}
if(vertex == start && visitFlag) //conducingt dfs for only one adjoining vertex in the given graph
break;
temp = temp->next;
}
}
2.3)printing results(第二張圖是對第一張圖的補充,我最后添加了 dfsPathSet 方法打印出上述dfs遍歷路徑所產生的集合):