数据结构-图的遍历(搜索)

127 阅读3分钟

前言

相较于树的遍历,图的遍历要复杂得多。因为图在遍历过程中,很可能会出现最后又回到一开始遍历的顶点的情况,而且也可能会出现图遍历之后没有遍历到图中一些顶点的情况。图的遍历主要有两种形式,分别是深度优先搜索(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需要一个栈用来实现递归回退操作,所以空间复杂度为O(V)O(V)级。

时间复杂度:

  • 在邻接矩阵形式下,访问每个顶点的邻接顶点的时间复杂度为O(V)O(V),由于有VV个顶点,所以总时间复杂度为O(V2)O(V^2)
  • 在邻接表形式下,设图中共有EE个边,所以访问所有顶点的邻接顶点的时间复杂度为O(E)O(E),又因为访问所有顶点的时间复杂度为O(V)O(V),所以总时间复杂度为O(E+V)O(E+V)

广度优先搜索(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需要一个队列来实现层序遍历,所以空间复杂度为O(V)O(V)级。

时间复杂度:

  • 和DFS一样,在邻接矩阵形式下,BFS的时间复杂度为O(V2)O(V^2)
  • 在邻接表形式下,每条边和每个顶点都会被访问一次,所以总时间复杂度为O(E+V)O(E+V)