图的深度优先遍历算法
更正一下前面一篇博文中的不妥之处:前面一篇博文有非无向图而不可广度优先遍历之嫌。其实广度优先搜索也是可以用在有向图中的,因为无向图是特殊的有向图,而这里广度优先遍历为的是遍历连通图的所有节点,对于有向图来说只要存在一条有向边,那么两个节点就是“连接”的状态,那么遍历就可以缠绕在这条边上。
深度优先遍历,也就是尽量深的遍历图的节点,同样用到了三色标记,用到了递归函数,这里还有一个盖时间戳的操作。当发现某个节点时记录一下时间,当完成一个节点的遍历时记录一下时间,这样依据时间戳,可以发现很多性质和应用。
其实在很多地方广度优先搜索和深度优先搜索代码实现起来都有特点:广度优先搜索使用一个queue来组织循环,而深度优先搜索则是使用递归函数来实现“深度”优先。
深度优先遍历的第一个应用是拓扑排序:
/** * 这里使用邻接矩阵表示一个无向连通图。 */ #include <iostream> #include <list> #include <algorithm> using namespace std; #define LEN 10 #define NIL -1 bool m[LEN][LEN]; enum COLOR {WHITE, GRAY, BLACK}; COLOR color[LEN]; int d[LEN]; int p[LEN]; int t; int f[LEN]; list<int> topolist; void DFS_VISIT(int u){ color[u] = GRAY; t++; d[u] = t; int i; for(i=0;i<LEN;++i){ if(m[u][i] == true){ if(color[i] == WHITE){ p[i] = u; DFS_VISIT(i); } } } color[u] = BLACK; f[u] = t = t +1; topolist.push_front(u); //cout<<u<<endl; } void DFS(){ int i; for(i=0;i<LEN;++i){ color[i] = WHITE; p[i] = NIL; } t = 0; for(i=0;i<LEN;++i) if(color[i] == WHITE) DFS_VISIT(i); } void printPath(int s, int v){ if(v == s) cout<<s<<endl; else if(p[v] == NIL){ cout<<"No path exists"<<endl; return ; } else{ printPath(s, p[v]); cout<<v<<endl; } } void topological_sort(){ DFS(); } void print(int s){ cout<<s<<endl; } int main(){ int i,j; for(i=0;i<LEN;++i){ for(j=0;j<LEN;++j){ m[i][j] = false; } } m[0][2] = true; m[2][0] = true; m[1][7] = true; m[7][1] = true; m[2][7] = true; m[7][2] = true; m[2][4] = true; m[4][2] = true; m[7][3] = true; m[3][7] = true; m[3][4] = true; m[4][3] = true; m[4][5] = true; m[5][4] = true; m[5][8] = true; m[8][5] = true; m[8][6] = true; m[6][8] = true; m[8][9] = true; m[9][8] = true; //DFS(); //printPath(0, 9); topological_sort(); for_each(topolist.begin(), topolist.end(), print); return 0; }
利用DFS进行拓扑排序,只需要在遍历节点完成记录时间戳时将该节点插入到一个用于记录拓扑序列的list前端就ok了。
深度优先搜索的第二个应用是:对强连通分支的划分
算法的过程很简单,分为以下两部:
首先调用DFS获得图G的节点的完成时间记录,
再对图G的转置进行深度优先遍历,这个时候根据第一步得到的完成时间记录降序进行遍历。
这样完成之后就得到了图G的几个强连通分支。