数据结构和算法 从0到1:图的深度优先搜索DFS

    技术2026-04-16  5

    1、概念

    图的深度优先搜索属于图的遍历的一种。

    1)图的遍历:

    从图中的某一顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次。 注意:树是一种特殊的图,所以树的遍历实际上也可以认为是一种特殊的图的遍历。 在图的遍历过程中,必须记下每个已经访问过的顶点,因此可以设一个辅助数组visited[]来标记顶点是否被访问过。 遍历分为:广度优先搜索和深度优先搜索。

    2)深度优先搜索(Depth-First-Search,DFS)

    类似于走迷宫,从迷宫开始一直走走到迷宫出口。 搜索策略是尽可能深地搜索一个图。 必要时要考虑剪枝。DFS需要回溯。

    3)深度优先的生成树和生成森林

    对于连通图,深度优先搜索会产生一颗深度优先生成树; 对于非连通图产生的是深度优先生成森林 基于邻接表存储的深度优先生成树不是唯一的。

    4)算法应用

    环路的存在性判断 拓扑排序 强连通分量

    2、算法

    首先访问图中某一起始点v,然后由v出发,访问与v邻接且未被访问的任一顶点w1,再访问与w1邻接 且未被访问的任一顶点w2…重复上述过程。当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该点开始继续上述搜索过程,直至图中所有顶点均被访问过为止。 伪代码如下:

    bool visited[MAX_VERTEX_NUM];//访问标记数组 void DFSTraverse(Graph G){ //对图G进行深度优先遍历 for (i = 0;i < G.vexnum;++i) //G.vexnum为顶点的数量 visited[i] = FALSE;//初始化访问标记数组 for (i=0;i < G.vexnum;++i)//从0号顶点开始遍历 if (!vistited[i]) //对每个连通分量调用一次DFS DFS(G,i);//vi未被访问过,从vi开始DFS } void DFS(Graph G,int v){ //从顶点v出发,深度优先遍历图G visit(v); //访问初始顶点v visited[v] = TRUE; //对v做已访问标记 for (w ∈ G.Adj(v)){ // 遍历v的所有邻接顶点 if (!visited[w]){ //w为v的未被访问的邻接顶点 DFS(G,w); } } }

    举例: 对于下图,运用深度优先搜索 定义辅助数组: 伪代码如下:

    DFS(G) 输入:图G 输出:祖先数组pred[], 发现时刻d, 结束时刻f 新建一维数组color[1... | V |], pred[1.. | V |], d[1... | V |],f[1... | V |] // 初始化 for u ∈ V do color[u] <- WHITE pred[u] <- NULL end time <- 0 // 保证搜索完全 for u ∈ V do if color[u] == WHITE then DFS_Visit(G, u) end end return pred, d, f void DFS_Visit(G, v){ // 修改当前顶点颜色、发现时刻 color[v] <- GRAY time <- time + 1 d[v] <- time // 搜索相邻顶点 for w ∈ G.Adj[v] do if color[w] == WHITE then pred[w] <- v DFS_Visit(G, w) end end // 结束搜索 color[v] <- BLACK time <- time + 1 f[v] <- time }

    3、时间复杂度

    采用邻接表存储方式时,O(V+E),V、E分别为顶点数、边数 采用邻接矩阵存储方式时,O(V^2)

    Processed: 0.012, SQL: 9