前言
相较于树的遍历,图的遍历要复杂得多。因为图在遍历过程中,很可能会出现最后又回到一开始遍历的顶点的情况,而且也可能会出现图遍历之后没有遍历到图中一些顶点的情况。图的遍历主要有两种形式,分别是深度优先搜索(DFS)和广度优先搜索(BFS)。这两种方法遍历均需要定义一个一维数组,用于记录顶点是否遍历过。
深度优先搜索(DFS)
原理
DFS类似于树的先序遍历,都是从一个结点开始沿着next指针尽可能深入的遍历结点,并且记录下来每一个遍历的结点。
具体的话,首先从图中任意一个顶点出发,输出当前顶点的信息后继续遍历和该顶点邻接的任意顶点,以此类推,直到无法继续遍历顶点为止(下一个顶点已经被遍历或者是该顶点没有可以遍历的邻接顶点)。之后回退操作,直到当前顶点仍然有尚未遍历过的邻接顶点或者所有顶点遍历完成,如果仍有尚未遍历的顶点,继续沿着该顶点深搜。总之,基本和树的前序遍历类似,只不过每遍历了一次顶点,要将该顶点信息记录下来。
实现
DFS的基本代码实现如下:
//以下有多个操作为函数接口,代码仅用于模拟实现操作,类似于伪代码
#define maxSize 50
bool visited[maxSize];
void DFST(Graph g) {
for(v=0;v<G.vexnum;++v)
visited[v]=false;
for(v=0;v<G.vexnum;++v)
if(!visited[v]) DFS(G,v);
}
void DFS(Graph g,int v) {
visit(v);
visited[v]=true;
for(w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)
if(!visited[w]) DFS(G,w);
}
性能
空间复杂度:由于DFS需要一个栈用来实现递归回退操作,所以空间复杂度为级。
时间复杂度:
- 在邻接矩阵形式下,访问每个顶点的邻接顶点的时间复杂度为,由于有个顶点,所以总时间复杂度为。
- 在邻接表形式下,设图中共有个边,所以访问所有顶点的邻接顶点的时间复杂度为,又因为访问所有顶点的时间复杂度为,所以总时间复杂度为。
广度优先搜索(BFS)
原理
如果说DFS类似于树的先序遍历,那么BFS就类似于树的层序遍历。不同的是,每访问完一个顶点,要将该访问的顶点记录下来。
从一个顶点出发,先遍历该顶点的所有邻接顶点,之后按照上一轮遍历的邻接顶点的顺序再遍历邻接顶点的邻接顶点,以此类推。如果访问全部完成后仍有尚未访问的顶点,再延该顶点继续进行BFS。
实现
BFS的基本代码实现如下:
#define maxSize 50
bool visited[maxSize];
void BFST(Graph g) {
for(v=0;v<G.vexnum;++v)
visited[v]=false;
new_queue(Q);
for(v=0;v<G.vexnum;++v)
if(!visited[v]) BFS(G,v);
}
void BFS(Graph g,int v) {
visit(v);
visited[v]=true;
Enqueue(Q,v);
while(!isEmpty) {
Dequeue(Q,v);
for(w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)
if(!visited[w]){
visited(w);
visited[w]=true;
Enqueue(Q,w);
}
}
}
性能
空间复杂度:由于BFS需要一个队列来实现层序遍历,所以空间复杂度为级。
时间复杂度:
- 和DFS一样,在邻接矩阵形式下,BFS的时间复杂度为。
- 在邻接表形式下,每条边和每个顶点都会被访问一次,所以总时间复杂度为。