深度优先遍历(Depth_First_Search),也有称为深度优先搜索,简称为DFS。
图7-5-2
反应快的同学一定会感觉到,深度优先遍历其实就是一个递归的过程,
如果再敏感一些,会发现其实转换成如图7-5-2的右图后,就像是一棵树的前序遍历,
没错,它就是。它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。
事实上,我们这里讲到的是连通图,对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
//如果我们用的是邻接矩阵的方式,则代码如下:
/* Boolean是布尔类型,其值是TRUE或FALSE */
typedef int Boolean;
/* 访问标志的数组 */
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; i < G.numVertexes; i++)
/* 初始所有顶点状态都是未访问过状态 */
visited[i] = FALSE;
for (i = 0; i < G.numVertexes; i++)
/* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */
if (!visited[i])
DFS(G, i);
}
//如果图结构是邻接表结构,其DFSTraverse函数的代码是几乎相同的,只是在递归函数中因为将数组换成了链表而有不同,代码如下。
/* 邻接表的深度优先递归算法 */
void DFS(GraphAdjList GL, int i)
{
EdgeNode *p;
visited[i] = TRUE;
/* 打印顶点,也可以其他操作 */
printf("%c ", GL->adjList[i].data);
p = GL->adjList[i].firstedge;
while (p)
{
if (!visited[p->adjvex])
/* 对为访问的邻接顶点递归调用 */
DFS(GL, p->adjvex);
p = p->next;
}
}
/* 邻接表的深度遍历操作 */
void DFSTraverse(GraphAdjList GL)
{
int i;
for (i = 0; i < GL->numVertexes; i++)
/* 初始所有顶点状态都是未访问过状态 */
visited[i] = FALSE;
for (i = 0; i < GL->numVertexes; i++)
/* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */
if (!visited[i])
DFS(GL, i);
}
对比两个不同存储结构的深度优先遍历算法,
- 对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此都需要O(n2)的时间。
- 而邻接表做存储结构时,找邻接点所需的时间取决于顶点和边的数量,所以是O(n+e)。
显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。
对于有向图而言,由于它只是对通道存在可行或不可行,算法上没有变化,是完全可以通用的。这里就不再详述了。