首页 > 其他分享 >图的遍历-DFS

图的遍历-DFS

时间:2024-04-04 20:00:26浏览次数:28  
标签:遍历 DFS 访问 搜索 邻接 回退 顶点

1. DFS遍历

DFS 算法的思想:对一个无向连通图,在访问图中某一起始顶点 v 后,由 v 出发,访问它的某一邻接顶点 w1;再从 w1 出发,访问与 w1 邻接但还没有访问过的顶点 w2;然后再从 w2 出发,进行类似的访问;…;如此进行下去,直至到达所有邻接顶点都被访问过的顶点 u 为止;接着,回退一步,回退到前一次刚访问过的顶点,看是否还有其它没有被访问过的邻接顶点,如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;如果没有,就再回退一步进行类似的访问。重复上述过程,直到该连通图中所有顶点都被访问过为止。 

对上图示的无向连通图, 采用 DFS 思想搜索的过程为(在图(a)中,实线表示前进方向,虚线表示回退方向,箭头旁的数字跟下面的序号对应):

  1. 从顶点 A 出发,访问顶点序号最小的邻接顶点,即顶点 B;
  2. 然后访问顶点 B 的未访问过的、顶点序号最小的邻接顶点,即顶点 C;
  3. 接着访问顶点 C 的未访问过的、顶点序号最小的邻接顶点,即顶点 G;
  4. 此时顶点 G 已经没有未访问过的邻接顶点了,所以回退到顶点 C; 
  5. 回退到顶点 C 后,顶点 C 也没有未访问过的邻接顶点了,所以继续回退到顶点 B;
  6. 顶点 B 还有一个未访问过的邻接顶点,即顶点 E,所以访问顶点 E;
  7. 然后访问顶点 E 的未访问过的、顶点序号最小的邻接顶点,即顶点 F;
  8. 顶点 F 有两个未访问过的邻接顶点,选择顶点序号最小的,即顶点 D,所以访问 D;
  9. 此时顶点 D 已经没有未访问过的邻接顶点了,所以回退到顶点 F;
  10. 顶点 F 还有一个未访问过的邻接顶点,即顶点 H,所以访问顶点 H;
  11. 然后访问顶点 H 的未访问过的、顶点序号最小的邻接顶点,即顶点 I;
  12. 此时顶点 I 已经没有未访问过的邻接顶点了,所以回退到顶点 H;
  13. 回退到顶点 H 后,顶点 H 也没有未访问过的邻接顶点了,所以继续回退到顶点 F;
  14. 回退到顶点 F 后,顶点 F 也没有未访问过的邻接顶点了,所以继续回退到顶点 E;
  15. 回退到顶点 E 后,顶点 E 也没有未访问过的邻接顶点了,所以继续回退到顶点 B;
  16. 回退到顶点 B 后,顶点 B 也没有未访问过的邻接顶点了,所以继续回退到顶点 A。

回退到顶点 A 后,顶点 A 也没有未访问过的邻接顶点了,而且顶点 A 是搜索的起始顶点。至此,整个搜索过程全部结束。由此可见, DFS 搜索最终要回退到起始顶点,并且如果起始顶点没有未访问过的邻接顶点了,则算法结束。 

2. DFS 算法的实现 

假设有函数 DFS(v), 可实现从顶点 v 出发访问它的所有未访问过的邻接顶点。 在 DFS 算法中,必有一数组,设为 visited[n],用来存储各顶点的访问状态。如果 visited[i] = 1,则表示顶点 i 已经访问过;如果 visited[i] = 0,则表示顶点 i 还未访问过。初始时,各顶点的访问状态均为 0。 

如果用邻接表存储图,则 DFS 算法实现的伪代码如下: 

DFS( 顶点 i ) //从顶点 i 进行深度优先搜索
{
    visited[ i ] = 1; //将顶点 i 的访问标志置为 1
    p = 顶点 i 的边链表表头指针;
    while( p 不为空 )
    {
        //设指针 p 所指向的边结点所表示的边中,另一个顶点为顶点 j
        if( 顶点 j 未访问过 )
        {
            //递归搜索前的准备工作需要在这里写代码
            DFS( 顶点 j );
            //以下是 DFS 的回退位置,在很多应用中需要在这里写代码
        }
        p = p->nextarc; //p 移向下一个边结点
    }
}

如果用邻接矩阵存储图(设顶点个数为 n,则 DFS 算法实现的伪代码如下: 

DFS( 顶点 i ) //从顶点 i 进行深度优先搜索
{
    visited[ i ] = 1; //将顶点 i 的访问标志置为 1
    for( j=0; j<n; j++ ) //对其他所有顶点 j
    {
        //j 是 i 的邻接顶点,且顶点 j 没有访问过
        if( Edge[i][j]==1 && !visited[j] )
        {
            //递归搜索前的准备工作需要在这里写代码
            DFS( j ) //从顶点 j 出发进行 DFS 搜索
            //以下是 DFS 的回退位置,在很多应用中需要在这里写代码
        }
    }
}

在上述伪代码中,在递归调用 DFS 函数前后的两个位置特别重要:

  • 如果需要在递归搜索前做一些准备工作,则需要在 DFS 递归调用前的位置写代码;
  • 如果需要在搜索的回退后做一些还原工作,或者根据搜索结果做一些统计或计算工作,则需要在 DFS 递归调用后的位置写代码。 

DFS的算法度分析:

现以下图(a)所示的无向图为例分析 DFS 算法的复杂度。设图中有 n 个顶点,有 m 条边。

如果用邻接表存储图,如图 (b)所示,从顶点 i 进行深度优先搜索,首先要取得顶点 i 的边链表表头指针,设为 p,然后要通过指针 p 访问它的第 1 个邻接顶点,如果该邻接顶点未访问过,则从这个顶点出发进行递归搜索;如果这个邻接顶点已经访问过,则指针 p 要移向下一个边结点。在这个过程中,对每个顶点递归访问 1 次,即每个顶点的边链表表头指针取出一次,而每个边结点都只访问了一次。由于总共有 2m 个边结点,所以扫描边的时间为 O(2m)。因此采用邻接表存储图时,进行深度优先搜索的时间复杂性为 O(n+2m)。 

如果采用邻接矩阵存储图,由于在邻接矩阵中只是间接的存储了边的信息。在对某个顶点进行 DFS 搜索时,要检查其他每个顶点,包括它的邻接顶点和非邻接顶点,所需时间为 O(n)。例如在下图(b)中,执行 DFS(A)时,要在邻接矩阵中的第 0 行检查顶点 A~I 与顶点 A 是否相邻且是否已经访问过。另外,整个 DFS 过程,对每个顶点都要递归进行 DFS 搜索,因此遍历图中所有的顶点所需的时间为 O(n2)。 

标签:遍历,DFS,访问,搜索,邻接,回退,顶点
From: https://www.cnblogs.com/love-9/p/18114543

相关文章

  • DFS 全排列问题 C语言代码
    深度优先搜索(DFS)是一种遍历算法,尽可能深地向子树中的结点搜索,直到达到一定的深度,再回溯到上层的结点,继续搜索未被访问的结点。全排列问题给定4个数1234,求他们所有可能的排列结果。代码#include<stdio.h>voiddfs(intx);inti;inta[4];intresult[4];/......
  • 11.python的字典dict(下) 遍历字典,结构优化
    11.python的字典dict(下)遍历所有的键值对items()方法是字典的一个内置方法,用于返回字典中所有键值对的视图(view)。它返回一个可迭代的对象,每个元素都是一个包含键和对应值的元组。下面用一个例子来说明items()方法的用法:dict1={'name':'John','age':25,'job':'En......
  • DFS:深搜+回溯+剪枝解决排列、子集问题
                      创作不易,感谢三连支持!! 一、全排列I.-力扣(LeetCode)classSolution{public://全局变量vector<vector<int>>ret;vector<int>path;boolcheck[6];vector<vector<int>>permute(vecto......
  • 二叉树的高效非递归层次遍历:一种O(n)时间复杂度与固定空间复杂度的解决方案
    @TOC在计算机科学中,二叉树是一种非常重要的数据结构,它在算法设计和问题解决中扮演着关键角色。本文将探讨如何使用非递归方法遍历一个给定的二叉树,并在不修改树结构的前提下,输出每个节点的关键字。这个过程将在O(n)的时间复杂度内完成,并且只使用固定量的额外存储空间。1.......
  • dfs 序求 LCA!
    前言为什么用dfs序求LCA而不用欧拉序?帅常数小,也就一半好玩反正没什么正经理由。正文定义dfs序是指对树进行深度优先遍历后得到的节点序列。\(\mathit{dfn}_i\)是节点\(i\)在dfs序中的位置(从\(0\)或\(1\)开始无影响)。LCA是最近公共祖先。深度\(\ma......
  • dfs 序求 LCA!
    前言为什么用dfs序求LCA而不用欧拉序?帅常数小,也就一半好玩反正没什么正经理由。正文定义dfs序是指对树进行深度优先遍历后得到的节点序列。\(\mathit{dfn}_i\)是节点\(i\)在dfs序中的位置(从\(0\)或\(1\)开始无影响)。LCA是最近公共祖先。深度\(\ma......
  • python 遍历字典
    在Python中,遍历字典(dictionary)通常涉及遍历字典的键(keys)、值(values)或者同时遍历键和值。以下是几种常见的遍历字典的方法:遍历字典的键(keys):pythonmy_dict={'a':1,'b':2,'c':3}forkeyinmy_dict.keys():print(key)遍历字典的值(values):pythonforvalue......
  • 15天【代码随想录算法训练营34期】第六章 二叉树 part02(● 层序遍历 10 ● 226.翻
    层序遍历10102.二叉树的层序遍历(opensnewwindow)#Definitionforabinarytreenode.#classTreeNode:#def__init__(self,val=0,left=None,right=None):#self.val=val#self.left=left#self.right=rightclassSolution......
  • 文件下载中的目录遍历漏洞与解决
    安全组觉得我们文件下载不够安全。给了份修复方案1、净化数据:对用户传过来的文件名参数进行硬编码或统一编码,对文件类型进行白名单控制,对包含恶意字符或者空字符的参数进行拒绝。2、web应用程序可以使用chroot环境包含被访问的web目录,或者使用绝对路径+参数来访问文件目录,时使其......
  • 大数据实验统计-1、Hadoop安装及使用;2、HDFS编程实践;3、HBase编程实践;4、MapReduce编
    大数据实验统计1、Hadoop安装及使用;一.实验内容Hadoop安装使用:1)在PC机上以伪分布式模式安装Hadoop;2)访问Web界面查看Hadoop信息。二.实验目的1、熟悉Hadoop的安装流程。2、熟悉Hadoop访问Web界等基本操作。大数据实验一,Hadoop安装及使用-CSDN博客文章浏览阅读149次,点赞3......