关于图的遍历,通常有深度优先搜索(DFS)和广度优先搜索(BFS),本文结合一般的图结构(邻接矩阵和邻接表),给出两种遍历算法的模板
1.深度优先搜索(DFS)
#include<iostream> #include<unordered_map> #include<queue> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<sstream> #include<set> #include<map> using namespace std; #define VERTEX_SIZE 100 //定义图中顶点数目 bool visited[VERTEX_SIZE];//建立顶点标记数组,用于判断顶点是否已经走过 class Graph{ .... //自定义图结构(邻接矩阵或者邻接表) }; void DFSTraverse(Graph G) { memset(visited,false,sizeof(visited));//初始化顶点标记数组 for(int i = 0 ; i < VERTEX_SIZE;i++) //遍历图中的每个连通分量 if(!visited[i]) DFS(G,i); } void DFS(Graph G,int v) { visited[v] = true;//修改标记 visit(v);//访问顶点v for(int w = FirstAdjVex(G,v); w >= 0;w = NextAdjVex(G,v,w))//寻找顶点v的邻接点 if(!visited[w]) DFS(G,w); } void visit(int v) { //自定义操作 ; } int main() { ... }
关于这个模板,有几点需要注意的:
(1)此处的模板适用于以邻接表表示的图结构或者以邻接矩阵表示的图结构,若以邻接表表示的话,时间复杂度为O(n+e);若以邻接矩阵表示的话,时间复杂度为O(n^2)。其中n为图中节点的个数,e为边的个数。
(2)遍历图的限制条件比较少,只要是未访问过(visited[v] == false)就可以进行访问。
(3)寻找顶点v的邻接点那部分代码是伪代码,需要根据图的具体表示结构进行寻找(邻接矩阵找到相应行进行遍历;邻接表遍历相应的单链表)
(4)在函数DFSTraverse()中,加入了个for循环,目的是:如果图是非连通图的话,需要遍历每个连通分支。由此,可以利用DFS来判断图的连通性,如果从某个节点开始遍历(任意节点),能遍历到所有节点的话,俺么这个图就是连通的。相当于在上述模板中把DFSTraverse()函数中的for循环换成单次遍历。(参考《王道》P191)
2.广度优先搜索(BFS)
#include<iostream> #include<unordered_map> #include<queue> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<sstream> #include<set> #include<map> using namespace std; #define VERTEX_SIZE 100 //定义图中顶点数目 bool visited[VERTEX_SIZE];//建立顶点标记数组,用于判断顶点是否已经走过 class Graph{ ....//自定义图结构(邻接矩阵或者邻接表) }; void BFSTraverse(Graph G) { memset(visited,false,sizeof(visited));//初始化顶点标记数组 queue<int> Q; for(int i = 0 ; i < VERTEX_SIZE;i++) if(!visited[i]) BFS(G,i,Q); } void BFS(Graph G,int v,queue<int> &Q) { visited[v] = true; visit(v);//访问顶点v Q.push(v);//v入队 while(!Q.empty()) { int u = Q.front(); Q.pop(); for(int w = FirstAdjVex(G,u); w >= 0;w = NextAdjVex(G,u,w))//找到顶点w的所有邻接点 if(!visited[w])//访问w的邻接点,并且访问过后将邻接点入队 { visited[w] = true; visit(w); Q.push(w); } } } void visit(int v) { //自定义操作 ; } int main() { ... }
(1)BFS的时间复杂度与DFS是一样的。
(2)BFSTraverse()函数中的for循环和DFSTraverse()中的作用是一样的,都是遍历所有的连通分支,只不过遍历的顺序不同;所以同样可以用BFS来判断图的连通性。
(3)队列存储的顶点都是它们本身已经访问过的但它们的邻接点未被访问过。