从图中某一顶点出发访遍图中其与顶点,且使每一个顶点仅被访问一次,这一过程叫做图的遍历(Traversing Graph)。
对于图的遍历来说,如何避免回路陷入死循环,就要科学设计遍历方案,通常有两种:深度优先遍历和广度优先遍历。
深度优先遍历
深度优先遍历(Depth_First_Search),也称为深度优先搜索,简称DFS。深度优先遍历从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点触发深度优先遍历图,直到图中所有和v有路径相同的顶点都被访问到。(非连通图)若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。
其实深度优先遍历是一个递归的过程。下面详细介绍具体的代码实现:
typedef int Boolean; //布尔类型,true or false
Boolean visited[MAX]; //访问标志的数组
/*邻接矩阵/邻接表的深度优先递归算法*/
void DFS(MGraph G,int i){ //邻接表(GraphAdjList GL,int i)
int j; //邻接表 EdgeNode *p;
visited[i]=TRUE;
printf("%c",G.vexs[i]); //邻接表 GL.adjList[i].data
for(j=0;j<G.numVertexes;j++){
if(G.arc[i][j]==1&&!visited[j])//若两个结点有边且该结点未被访问
DFS(G,j);
}
/*邻接表
p=GL.adjList[i].firstedge //获取边表结点
while(p){
if(!visited[p->adjvex])
DFS(GL,p->adjvex); //对未访问的顶点递归
p=p->next;
}
*/
}
/*邻接矩阵/邻接表的深度遍历操作*/
void DFSTraverse(MGraph G){ //邻接表(GraphAdjList GL)
int i;
for(i=0;i<G.numVertexes;i++) //邻接表 GL->numVertexes
visited[i]=FALSE; //初始化所有顶点为未访问状态
for(i=0;i<G.numVertexes;i++) //邻接表 GL->numVertexes
//对未访问顶点调用DFS,若是连通图只会调用一次。
if(!visited[i])
DFS(G,i); //邻接表 DFS(GL,i);
}
对比两种不同结构的深度优先遍历算法,对于n个点和e条边的图而言,邻接矩阵是二维数组,查找每个邻接点需要访问矩阵中的所有元素,所以时间复杂度为 O(n2) ,而对于邻接表来说,找邻接点取决于顶点和边的数量,时间复杂度为 O(n+e) ,显然对于点多边少的图,邻接表能大大提高时间效率。
广度优先遍历
广度优先遍历(Breadth_First_Search),也称为广度优先搜索,简称BFS。
若说图的深度优先遍历类似于树的前序遍历,则图的广度优先遍历就类似与树的层序遍历。下面详细介绍具体的代码实现:
/*邻接矩阵/邻接表的广度遍历操作*/
void BFSTraverse(MGraph G){ //邻接表(GraphAdjList GL)
int i,j; //邻接表 int i; EdgeNode *p;
Queue Q; //队列
for(i=0;i<G.numVertexes;i++) //邻接表 GL->numVertexes
visited[i]=FALSE; //初始化所有顶点为未访问状态
InitQueue(&Q); //初始化队列
for(i=0;i<G.numVertexes;i++) {//邻接表 GL->numVertexes
if(!visited[i]){ //若未访问
visited[i]=TRUE;
//打印顶点
printf("%c",G.vexs[i]); //邻接表 GL->adjList[i].data
EnQueue(&Q,i);
while(! QueueEmpty(Q)){
DeQueue(&Q,&i);
for(j=0;j<G.numVertexes;j++){
/*判断其他定点若与当前结点存在边并未被访问过,入队列*/
if(G.arc[i][j]==1&&!visited[j]){
visited[j]=TRUE;
printf("%c",G.vexs[j]); //打印顶点
EnQueue(&Q,i);
}
}
/*邻接表
p=GL->adjList[i].firstedge; //当前顶点的边表头指针
while(p){
if(!visited[p->adjvex]){ //当前结点未被访问,入队
visited[p->adjvex]=TRUE;
printf("%c",GL->adjList[p->adjvex].data);
EnQueue(&Q,p->adjvex);
}
p=p->next; //指针指向下一邻接点
}
*/
}
}
}
}
对比图的深度优先遍历还是广度优先遍历,发现,它们在时间复杂度上是一样的,不同之处仅在于对顶点访问顺序不同。相比较而言,深度优先更适合于目标比较明确,以找到目标为主要目的地情况;而广度优先遍历更适合在不断扩大遍历范围时找到相对最优解的情况。