DFS算法

大概


深度优先搜索(Depth-First-Search)是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。


深度优先搜索是一种在开发爬虫早期使用较多的方法。。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。优点是能遍历一个Web 站点或深层嵌套的文档集合;缺点是因为Web结构相当深,,有可能造成一旦进去,再也出不来的情况发生。


事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.


深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。


特点


     


每次深度优先搜索的结果必然是图的一个连通分量。深度优先搜索可以从多点发起。如果将每个节点在深度优先搜索过程中的“结束时间”排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的“拓扑排序”,即topological sort.


当然,当人们刚刚掌握深度优先搜索的时候常常用它来走迷宫。事实上我们还有别的方法,那就是广度优先搜索 (BFS)。状态(state):状态是指问题求解过程中每一步的状况。


算符(operater)算符是把问题从一种状态变换到另一种状态的方法代号。算符的取值范围就是搜索的范围。(一般设为局部变量)。


节点(node):用来表明状态特征及相关信息。


效率


1.作为搜索算法的一种,DFS对于寻找一个解的NP(包括NPC)问题作用很大。但是,搜索算法毕竟是时间复杂度是O(n!)的阶乘级算法,它的效率比较低,在数据规模变大时,这种算法就显得力不从心了。


2.关于深度优先搜索的效率问题,有多种解决方法。最具有通用性的是剪枝(prunning),也就是去除没有用的搜索分支。有可行性剪枝和最优性剪枝两种。此外,对于很多问题,可以把搜索与动态规划(DP,dynamic programming)、完备匹配(匈牙利算法)等高效算法结合。


算法详解


1.首先选定图的类别(有向图、无向图),再选定图的存储结构,根据输入的顶点或者边建立图;并把相应的邻接表或者邻接矩阵输出;


2.根据已有的邻接矩阵或邻接表用递归方法编写深度优先搜索遍历算法,并输出遍历结果;


图的深度遍历原则:


1 如果有可能,访问一个领接的未访问的节点,标记它,并把它放入栈中。


2 当不能执行规则 1 时,如果栈不为空,则从栈中弹出一个元素。


3 如果不能执行规则 1 和规则 2 时,则完成了遍历。


代码中的图使用的是Graph 图-邻接矩阵法 来表示,其他的表示法请见:Graph 图-邻接表法


代码中的Stack为辅助结构,用来记载访问过的节点。栈的详细描述可以见:ArrayStack 栈 ,LinkedStack 栈 。


Vertex表示图中的节点,其中包含访问,是否访问,清除访问标志的方法。  Graph.main:提供简单测试。代码可以以指定下标的节点开始作深度遍历。  代码比较简单,除了Graph.dsf(int i)深度优先遍历算法外没有过多注释。


深度优先搜索 DFS的过程


正如算法名称那样,深度优先搜索所遵循的搜索策略是尽可能“深”地搜索图。在深度优先搜索中,对于最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续汉下去。当结点v的所有边都己被探寻过,搜索将回溯到发现结点v有那条边的始结点。这一过程一直进行到已发现从源结点可达的所有结点为止。如果还存在未被发现的结点,则选择其中一个作为源结点并重复以上过程,整个进程反复进行直到所有结点都被发现为止。


和宽度优先搜索类似,每当扫描已发现结点u的邻接表从而发现新结点v时,深度优先搜索将置v的先辈域π[v]为u。和宽度优先搜索不同的是,前者的先辈子图形成一棵树,而后者产生的先辈子图可以由几棵树组成,因为搜索可能由多个源顶点开始重复进行。因此深度优先搜索的先辈子图的定义也和宽度优先搜索稍有不同: Gπ=(V,Eπ),Eπ={(π[v],v)∈E:v∈V∧π[v]≠NIL}


深度优先搜索的先辈子图形成一个由数个深度优先树组成的深度优先森林。Eπ中的边称为树枝。


和宽度优先搜索类似,深度优先在搜索过程中也为结点着色以表示结点的状态。每个顶点开始均为白色,搜索中被发现时置为灰色,结束时又被置成黑色(即当其邻接表被完全检索之后)。这一技巧可以保证每一顶点搜索结束时只存在于一棵深度优先树上,因此这些树都是分离的。


除了创建一个深度优先森林外,深度优先搜索同时为每个结点加盖时间戳。每个结点v有两个时间戳:当结点v第一次被发现(并置成灰色)时记录下第一个时间戳d[v],当结束检查v的邻接表时(并置v为黑色)记录下第二个时间截f[v]。许多图的算法中都用到时间戳,他们对推算深度优先搜索进行情况是很有帮助的。


下列过程DFS记录了何时在变量d[u]中发现结点u以及何时在变量f[u]中完成对结点u的检索。这些时间戳为1到2|V|之间的整数,因为对每一个v中结点都对应一个发现事件和一个完成事件。对每一顶点u,有 d[u]<f[u]


(1) 在时刻d[u]前结点u为白色,在时刻d[u]和f[u]之间为灰色,以后就变为黑色。 下面的伪代码就是一个基本的深度优先搜索算法,输人图G可以是有向图或无向图,变量time是一个全局变量,用于记录时间戳。


procedure DFS(G); -    begin


- 1    for 每个顶点u∈V[G] do


-         begin


- 2            color[u]←White;


- 3            π[u]←NIL;


-         end;


- 4    time←0;


- 5    for 每个顶点u∈V[G] do


- 6      if color[u]=White


- 7          then DFS_Visit(G,u);


- end; -


-  procedure DFS_Visit(G,u);


-   begin


- 1   color[u]←Gray;              Δ白色结点u已被发现


- 2   d[u]←time←time+1;


- 3   for 每个顶点v∈Adj[u] do     Δ探寻边(u,v)


- 4      if color[v]=White


-           then begin


- 5                π[v]←u;


- 6                DFS_Visit(G,v);


-                end;


- 7   color[u]←Black;             Δ完成后置u为黑色


- 8   f[u]←time←time+1;


- end;


- 图2说明了DFS在图1所示的图上执行的过程。被算法探寻到的边要么为阴影覆盖 (如果该边为树枝),要么成虚线形式 (其他情况)。对于非树枝的边,分别标明B(或F)以表示反向边、交叉边或无向边。我们用发现时刻Z完成时刻的形式对结点加盖时间戳。



DFS(Depth-First Search,深度优先搜索)是一种用于遍历或搜索树或图的算法。它的特点是从根节点开始,尽可能深地探索每个分支,在无法继续深入时回溯至上一节点并尝试其他路径。 ### DFS 算法的基本步骤: 1. **选择起始点**:从任意一个顶点作为起点。 2. **访问当前节点**:标记该节点已被访问,并记录相关信息(如存储数据、检查条件等)。 3. **递归处理相邻节点**:对于每一个未被访问过的邻居节点,递归调用 DFS 函数;若所有邻接点都已访问,则返回上一级。 4. **终止条件**:当整个图的所有节点都被访问完毕之后结束搜索过程。 #### 实现方式有两种常见形式: - **递归实现** ```python def dfs(graph, node, visited): if node not in visited: print(node) visited.add(node) # 标记为已访问 for neighbor in graph[node]: dfs(graph, neighbor, visited) # 示例调用 graph = { 'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['F'], 'D': [], 'E': ['F'], 'F': [] } visited = set() dfs(graph, 'A', visited) ``` - **非递归栈结构** ```python def iterative_dfs(start_v): discovered = [] stack = [start_v] while stack: v = stack.pop() if v not in discovered: discovered.append(v) for w in G[v]: stack.append(w) return discovered # 假设G是一个字典表示的无向图 G = {'A':['B','C'],'B':['A','D'],'C':['A','D'],'D':['B','C']} print(iterative_dfs('A')) ``` 在实际应用中,DFS 可以帮助解决许多问题,例如连通分量查找、拓扑排序以及迷宫求解等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值