广度优先遍历(Breadth-FirstTraversal)
1、广度优先遍历的递归定义
设图G的初态是所有顶点均未访问过。在G中任选一顶点v为源点,则广度优先遍历可以定义为:首先访问出发点v,接着依次访问v的所有邻接点w
1,w
2,…,w
t,然后再依次访问与w
l,w
2,…,w
t邻接的所有未曾访问过的顶点。依此类推,直至图中所有和源点v有路径相通的顶点都已访问到为止。此时从v开始的搜索过程结束。
若G是连通图,则遍历完成;否则,在图C中另选一个尚未访问的顶点作为新源点继续上述的搜索过程,直至G中所有顶点均已被访问为止。
广度优先遍历类似于树的按层次遍历。采用的搜索方法的特点是尽可能先对横向进行搜索,故称其为广度优先搜索(Breadth-FirstSearch)。相应的遍历也就自然地称为广度优先遍历。
2、广度优先搜索过程
在广度优先搜索过程中,设x和y是两个相继要被访问的未访问过的顶点。它们的邻接点分别记为x
1,x
2,…,x
s和y
1,y
2,…,y
t。
为确保先访问的顶点其邻接点亦先被访问,在搜索过程中使用FIFO队列来保存已访问过的顶点。当访问x和y时,这两个顶点相继入队。此后,当x和y相继出队时,我们分别从x和y出发搜索其邻接点x
1,x
2,…,x
s和y
1,y
2,…,y
t,对其中未访者进行访问并将其人队。这种方法是将每个已访问的顶点人队,故保证了每个顶点至多只有一次人队。
3、广度优先搜索算法
(1)邻接表表示图的广度优先搜索算法
void BFS(ALGraph*G,int k)
{// 以v
k为源点对用邻接表表示的图G进行广度优先搜索
int i;
CirQueue Q; //须将队列定义中DataType改为int
EdgeNode *p;
InitQueue(&Q);//队列初始化
//访问源点v
k
printf(“visit vertex:%e”,G->adjlist[k].vertex);
visited[k]=TRUE;
EnQueue(&Q,k);//v
k已访问,将其人队。(实际上是将其序号人队)
while(!QueueEmpty(&Q)){//队非空则执行
i=DeQueue(&Q); //相当于v
i出队
p=G->adjlist[i].firstedge; //取v
i的边表头指针
while(p){//依次搜索v
i的邻接点v
j(令p->adjvex=j)
if(!visited[p->adivex]){ //若v
j未访问过
printf(“visitvertex:%c”,C->adjlistlp->adjvex].vertex); //访问v
j
visited[p->adjvex]=TRUE;
EnQueue(&Q,p->adjvex);//访问过的v
j人队
}//endif
p=p->next;//找v
i的下一邻接点
}//endwhile
}//endwhile
}//end of BFS
(2)邻接矩阵表示的图的广度优先搜索算法
void BFSM(MGraph *G,int k)
{以vk为源点对用邻接矩阵表示的图G进行广度优先搜索
int i,j;
CirQueue Q;
InitQueue(&Q);
printf(“visit vertex:%c”,G->vexs[k]); //访问源点vk
visited[k]=TRUE;
EnQueue(&Q,k);
while(!QueueEmpty(&Q)){
i=DeQueue(&Q); //vi出队
for(j=0;j<G->n;j++)//依次搜索vi的邻接点vj
if(G->edges[i][j]==1&&!visited[j]){//vi未访问
printf(“visit vertex:%c”,G->vexs[j]);//访问vi
visited[j]=TRUE;
EnQueue(&Q,j);//访问过的vi人队
}
}//endwhile
}//BFSM
(3) 广度优先遍历算法
类似于DFSTraverse。【参见DFSTraverse算法】
4、图的广度优先遍历序列
广度优先遍历图所得的顶点序列,定义为图的广度优先遍历序列,简称BFS序列。
(1)一个图的BFS序列不是惟一的
(2)给定了源点及图的存储结构时,算法BFS和BFSM所给出BFS序列就是惟一的
【例】上图中G7以邻接表为存储结构,以v0为出发点的BFS搜索过程和BFS序列【参见动画演示】
5、算法分析
对于具有n个顶点和e条边的无向图或有向图,每个顶点均入队一次。广度优先遍历(BFSTraverse)图的时间复杂度和DFSTraverse算法相同。
当图是连通图时,BFSTraverse算法只需调用一次BFS或BFSM即可完成遍历操作,此时BFS和BFSM的时间复杂度分别为O(n+e)和0(n2)。