图的遍历和树的遍历类似的,从图中某一顶点出发遍访图中其余顶点,且使每个顶点只被访问一次,这个过程叫做图的遍历。
图那么复杂,因为它的任一顶点都可能和其余的所有顶点相邻接,极有可能存在沿着某条路径搜索后,又回到原顶点,而有些顶点却还没有遍历到的情况。因此我们需要在遍历过程中把访问过的顶点打上标记,以避免访问多次而不自知。具体办法是设置-一个访问数组 visited[n], n是图中顶点的个数,初值为0,访问过后设置为1。
对于图的遍历来说,如何避免因回路陷入死循环,就需要科学地设计遍历方案,通常有两种遍历次序方案:它们是深度优先遍历和广度优先遍历。
深度优先遍历
深度优先遍历(Depth_First_Search),也有称为深度优先搜索,简称为DFS。
深度优先遍历其实就是一个递归的过程,如果再敏感一些,会发现其实转换成如图 的右图后,就像是一棵树的前序遍历,没错,
它就是。它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。事实上,我们这里讲到的是连通图,对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
如果我们用的是邻接矩阵的方式,代码如下:
typedef int Boolean; // Boolean是布尔类型,其值是TRUE或FALSE
Boolean visited[MAX]; //访问标志的数组
/*邻接矩阵的深度优先递归算法*/
void DFS(MGraph G, int i)
{
int j;
visited[i] = TRUE;
printf("%c",G.vexs[i]);//打印顶点, 也可以其他操作
for (j = 0; j < G.numvertexes; j++)
if (G.arc[i][j] == 1 && !visited[j])
DFS(G,j); //对为访问的邻接顶点递归调用
}
/*邻接矩阵的深度遍历操作*/
void DFSTraverse(MGraph G)
{
int i;
for (i = 0; 1 < G.numvertexes; i++)
visited[i] = FALSE; // 初始所有顶点状态都是未访问过状态
for (i = 0; i < G.numVertexes; i++)
if (!visited[i]) //对未访问过的顶点调用DFS,若是连通图,只会执行一次
DFS(G, 1);
}
广度优先遍历
广度优先遍历(Brcadth_First Search), 又称为广度优先搜索,简称BFS。广度优先遍历借助队列完成。
如果说图的深度优先遍历类似树的前序遍历,那么图的广度优先遍历就类似于树的层序遍历了。我们将下图的第一幅图稍微变形,变形原则是顶点A放置在最上第一层,让与它有边的顶点B、F为第二层,再让与B和F有边的顶点C、1、G、E为第三层,再将这四个顶点有边的D、H放在第四层,如图7-5-3的第二幅图所示。此时在视觉上感觉图的形状发生了变化,其实顶点和边的关系还是完全相同的。
邻接矩阵的广度优先遍历算法
void BFSTraverse(MGraph G)
{
int i, j;
Queue Q;
for (i = 0; i < G.numVertexes; i++)
visited[i] = FALSE:
InitQueue(&Q); //初始化一辅助用的队列
for (i = 0; i < G.numVertexes; i++) //对每一个顶点做循环
{
if (!visited[i]) //若是未访问过就处理
{
visited[1] = TRUE; //设置当前顶点访问过
printf("%c ",G.vexs[i]);//打印顶点
EnQueue(&Q, i); //将此顶点入队列
while (!QueueEmpty(Q)) //若当前队列不为空
{
DeQueue(&Q, &i); //将队中元素出队列,赋值给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, j); //将找到的此顶点入队列
}
}
}
}
}
}