【数据结构随笔】图的深度优先搜索(DFS)与广度优先搜索(BFS)
本文介绍图的两种重要遍历算法:深度优先搜索与广度优先搜索
一.深度优先搜索(DFS)
深度优先搜索是一个不断探查与回溯的过程,其思想是递归。树的先序遍历可以看成是深度优先搜索的一种情况。在探查的每一步中,算法都记录有一个当前顶点。最初的当前顶点,也即函数指定的开始顶点。在每一步的探查过程中,首先访问当前顶点v,并立刻将其访问标志visited[v]设为true。然后将与其邻接的任一还未访问过的顶点作为当前顶点,重复上述步骤。当当前顶点的的所有邻接顶点均已访问过后,退回到上一步时访问的顶点,并将其设为当前顶点,继续探查其是否还有未访问过的顶点。当所有顶点的visited均已设为true时,证明该连通图的所有顶点均已至少访问一遍,此时递归工作栈逐步退栈,程序结束。
给出一个用DFS遍历的示例:
给出图(a)的以0为最初顶点的DFS的工作过程:
从顶点0开始进行的深度优先遍历过程:0,1,(0),3,(1),7,(3),4,(1),(7),(back to 7),5,2,(0),6,(2),(7),(back to 2),(5),(back to 5),(7),(back to 4),(back to 7),(5),(6),(back to 3),(back to 1),(4),(back to 0),(2)end;
DFS的伪代码描述:
virtual void Graph::DFS(){
bool* visited=new bool[n];
for(int i=0;i<n;i++)
visited=false;
DFS(0);
delete []visited;
}
virtual void Graph::DFS(const int v){ //访问当前顶点v的所有未访问过的结点
visited[v]=true;
for(each vertex w adjacent to v)
DFS(w);
}
代码实现:
void DFS(Graphlnk& G, const int v) {
int loc;
int n = G.NumberOfVertex(); //图中顶点个数
bool* visited = new bool[n];
for (int i = 0; i < n; i++)
visited[i] = false;
loc = G.GetVertexPos(v); //获取v对应的结点在图中的位置
DFS(G, loc, visited); //递归子程序
delete[]visited;
}
void DFS(Graphlnk& G, const int v, bool* visited) {
std::cout << G.getValue(v) << " ";
visited[v] = true;
int w = G.getFirstNeighbor(v); //获取v的第一个邻接结点
while (w != -1) {
if (visited[w]==false)
DFS(G, w, visited);
w = G.getNextNeighbor(v, w); //获取v的邻接结点的下一个邻接结点
}
}
其中的图由邻接表实现;
DFS时间复杂度的分析:初始化标志数组visited[]的时间复杂度为O(n);
如果图以邻接矩阵的形式表示,那么探查当前顶点v的所有邻接结点的时间复杂度为O(n),因为需要遍历矩阵第v行的n个结点;由于有n个顶点,故总体时间复杂度为O(n²)
如果图以邻接链表的形式表示,则只要遍历当前顶点的链表即可,DFS访问每一个顶点最多一次,若图一共有e条边,时间复杂度为O(e+n).
二.广度优先搜索
与DFS不同,广度优先搜索(BFS)不是一个探查与回溯的过程,而是一个层次遍历的过程。在这个过程中,图有多少顶点就要重复多少步,每一步都有一个当前顶点。最初的当前顶点是由主过程指定的当前顶点。在每一步中,首先访问当前顶点v,并设置该顶点的访问标志visited为true,然后依次访问当前顶点的所有邻接顶点w1,w2,......wn,然后再按顺序依次访问w1,w2,......wn所有未访问的邻接顶点,依次类推直到图中的所有结点都被访问。
由于BFS不是一个递归的过程,故需要一个队列来记录正在访问的这一层以及上一层顶点,以便于继续向下访问。
依旧以上图(a)为例,我们给出其广度优先遍历的序列:
0,1,2,3,4,5,6,7
BFS的伪代码描述:
virtual void Graph::BFS(int v){
bool* visited=new bool[n];
fill(visited,visited+n,false);
queue<int>Q;
Q.push(v);
visited[v]=true;
while(!Q.empty()){
v=Q.front();
Q.pop();
for (all vertices w adjacent to v)
if(!visited[w]){
Q.push(w);
visited[w]=true;
}
}
delete []visited;
}
代码实现:
void BFS(Graphlnk& G, const int v) {
int w;
int n = G.NumberOfVertex();
bool* visited = new bool[n];
for (int i = 0; i < n; i++)
visited[i] = false;
int loc = G.GetVertexPos(v);
std::cout << G.getValue(loc) << " ";
visited[loc] = true;
std::queue<int>Q;
Q.push(loc);
while (!Q.empty()) {
loc = Q.front();
Q.pop();
w = G.getFirstNeighbor(loc);
while (w != -1) {
if (visited[w]==false) {
std::cout << G.getValue(w) << " ";
visited[w] = true;
Q.push(w);
}
w = G.getNextNeighbor(loc, w);
}
}
delete[]visited;
}
时间复杂度:同DFS;