DFS

图简介
给定一幅由节点和边组成的图G(V,E),V是顶点的集合,E表示边的集合。顶点是对现实对象的一种抽象,而边表示的是两个对象之间一定的关系。

图片

图在计算机中的存储方式有:矩阵存储(适用于稠密的图),邻接表存储(适合稀疏图)。

例:无向图的矩阵

图片说明

图又分有向图和无向图,无向图的矩阵表示是一个对称矩阵(M(i,j)=M(j,i)),有向图的矩阵则不一定对称。

图还有无权图和有权图 之分。无权图的矩阵里的数值只表示是否连通,而有权图矩阵里的数值大小可表示价值、距离、代价等现实意义。

图的遍历

当我们需要从图中获取信息时,我们就需要访问图的节点,用dfs的目的不唯一,可能是为了查找是否存在某个节点,可能是要数图中有多少个环,或者判断图能否一笔画等等。

重点是避免重复访问也不能漏掉一些节点。

图的遍历方式有深度优先遍历(DFS)和广度优先遍历(BFS)两种。本文只介绍深度优先遍历。

所谓DFS,通俗的理解就是,当我访问完一个节点(v1)后,我从与当前节点相邻且未访问过的节点集合(S)中拿出一个来,对选出来的这个节点(v2)我们重复与前一个节点相同的过程,直到没有满足条件的节点可以被访问,于是我们退回到v1处,从S中再选出一个未被访问过的节点进行上述过程,直到整张图的节点都被访问过。

以上的描述很明显具有递归结构。我们也知道,递归算法都可以用栈来实现。

DFS算法递归实现的框架如下:

1 procedure DFS(G, V) is 
2      将顶点V标记为 “已遍历”
3      对于 V 的所有邻接点 W 
4             如果 w 未被遍历过 则
5                  深度优先遍历 (G,W)

我们需要一个数组来记录某个节点是否访问过。

用栈来实现DFS算法

栈的特点是先进后出(Fisrt in last out), 利用这 个特点,我们可以在访问当前节点之后,把它所有当前未被访问的邻点入栈,然后弹出栈顶元素,即最后一个入栈的节点。对弹出的节点标记为已访问,然后同样把他的所有当前未被访问的邻点入栈(后入栈的节点会比先入栈的节点先出来,以此达到了深度优先遍历的目的)。这样一直进行下去,直到栈空。

procedure DFS-iterative(G, v) is
let S be a stack          //声明s为一个栈
S.push(v)                    //根节点(起始节点)入栈
while S is not empty do    //栈不空时一直循环
       v = S.pop()   //弹出栈顶元素赋值给v
       label v as visited   //标记v为已访问

        //v的所有未被访问的邻点中入栈
        for all edges from v to w in G.adjacentEdges(v) do

                 if w is unvisited S.push(w)

注意以上程序只能访问一个连通枝中的节点,若要访问一个不连通的图中的中的所有节点,只需要在该程序以外加一个大循环,从未被访问过的节点开始访问其他的连通枝即可。

复杂度分析:

DFS算法需要借助一个递归工作栈和一个标记是否访问的数组,故它的空间复杂度为O(N)。N为节点个数。
遍历图的过程实质上是对每个顶点查找其邻接点的过程,其耗费的时间取决于所采用结构。
邻接表表示时,查找所有顶点的邻接点所需时间为O(E),访问顶点的邻接点所花时间为O(N),此时,总的时间复杂度为O(N+E) 。

邻接矩阵表示时,查找每个顶点的邻接点所需时间为O(N) ,要查找整个矩阵,故总的时间度为 O(N^2) 。

全部评论

相关推荐

01-23 19:12
门头沟学院 Java
榨出爱国基因:你还差 0.1% 就拿到校招礼盒,快叫朋友给你砍一刀吧
投递拼多多集团-PDD等公司9个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务