深度优先搜索 DFS

本文详细介绍了深度优先搜索(DFS)的基本思想、算法实现,包括单点路径问题、连通分量问题的解决,并探讨了如何利用DFS检测环与解决双色问题。DFS在图论中扮演着重要角色,可用于判断顶点间连通性、寻找路径及划分连通分量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、基本思想

在搜索一幅图时,只需要用一个递归来遍历所有顶点。在访问其中一个顶点时:

  • 将它标记为已访问
  • 递归访问它的所有未标记的邻居顶点

DFS只会访问与起点s连通的所有顶点(而不会访问其他顶点)。如果图是连通的,则所有顶点都会被访问到。
DFS中每条边都会被访问两次,且在第二次时总会发现这个顶点已经被标记过了。

DFS解决了单点连通性的问题,使得用例可以判定其他顶点和给定的起点是否连通

2、算法实现

/*单点连通性*/
public class DepthFirstSearch{
   
   
    private boolean[] marked;    //使用一个boolean数组来记录和起点s连通的所有顶点
    private int count;          //与起点s连通的顶点数目

    public DepthFirstSearch(Graph G, int s){
   
   
        marked = new boolen[G.V()];
        dfs(G, s);    
    }
    
    private void dfs(Graph G, int v){
   
   
        count++;
        marked[v] = true;
        for (int w : G.adj(v)){
   
   
            if (!marked[w])
                dfs(G, w);
        }
    }

    public boolen marked(int v){
   
    return marked[v]; }
	public int count(){
   
    return count; }
}

下图显示算法轨迹,起点为顶点0。
1、因为顶点2是0的邻接表的第一个元素且没有被标记过,dfs()标记该位置并且访问顶点2。

2、接下来,顶点0是2的邻接表的第一个元素且已经被标记过了,因此dfs()跳过了它。然后,顶点1是2的邻接表的第二个元素且没有被标记,调用dfs()来标记并访问顶点1。

3、对于顶点1的访问和前面的有所不同:因为它的邻接表中所有顶点(0和2)都已经被标记过,因此不需要再进行递归,方法从dfs(1)返回,下一条被检查的边时2-3,因此调用dfs()标记并访问顶点3.

。。。。后续同理
这里写图片描述
深度优先搜索可处理问题:

  • 连通性。可以回答 “两个给定的顶点是否连通?” 或者 “找出图中所有连通分量” 等类似问题
  • 单点路径。可以回答 “从s到目的顶点v是否存在一条路径?如果有找出这条路径” 等类似问题

3、单点路径问题

图处理算法的设计模式:将图的表示实现分离开来。为每个处理图的任务创建一个相应的操作类,用例可以创建相应的对象来完成任务。
典型的用例程序会构造一幅图,将 图Graph 传递给实现了某个处理算法的类(作为其构造函数的参数),然后调用方法来获取图的各种性质。

根据上述设计模式,单点路径问题的API如下:
构造函数 Paths() 接受一个图和起点s作为参数,计算 s 到与s连通的所有顶点之间的路径。
在为起点s创建了Paths对象 后,用例可以调用 pathTo(v) 方法来遍历从s到v的路径上的所有顶点。
这里写图片描述

下述算法基于深度优先搜索实现了Paths操作类。它扩展了DepthFirstSearch,添加了一个实例变量 edgeTo[] 整型数组。根据这个数组可以回溯找到从每个与s连通的顶点回到s的路径。它会记住每个顶点到起点的路径。

/* 使用深度优先搜索查找图中的路径*/
public class DepthFirstPaths {
   
   
    private boolean[] marked
### 深度优先搜索算法 (DFS) 的实现及应用 #### 一、DFS 算法思想 深度优先搜索是一种用于遍历或搜索树或图的算法。该方法会尽可能深地探索子节点,当无法继续前进时则回退到上一个节点并尝试其他分支[^1]。 #### 二、DFS 算法实现 ##### (一)递归实现 通过函数调用来模拟栈的行为是最直观的方式之一来执行 DFS 。下面是一个简单的 Python 版本: ```python def dfs_recursive(graph, node, visited=None): if visited is None: visited = set() if node not in visited: print(node) visited.add(node) for neighbour in graph[node]: dfs_recursive(graph, neighbour, visited) ``` 此版本利用了Python内置的数据结构`set()` 来跟踪已访问过的顶点集合;每当遇到新的未被标记的位置就打印出来,并对其相邻位置重复相同的操作直至整个连通分量都被覆盖完毕。 ##### (二)非递归实现 为了更高效地控制内存分配以及处理大规模数据集的情况,可以采用显式的堆栈机制代替隐含于递归中的系统调用栈来进行迭代式 DFS : ```python from collections import deque def dfs_iterative(graph, start_node): stack, path = [start_node], [] while stack: vertex = stack.pop() if vertex in path: continue path.append(vertex) print(vertex) # Add all unvisited neighbors to the top of the stack. for neighbor in reversed(graph[vertex]): if neighbor not in path and neighbor not in stack: stack.append(neighbor) return path ``` 这里使用列表作为 LIFO 队列(即后进先出),每次取出最后一个加入队列里的元素进行扩展操作,同时记录下已经走过的路径防止循环访问同一节点多次。 #### 三、DFS 应用场景 - **图的遍历** 对给定无向图 G=(V,E),从任意起点出发按照某种顺序依次访问所有可达顶点的过程被称为广义上的 “遍历”。而基于 DFS 可以很容易完成这一任务,在实际编码过程中只需稍作修改即可适应不同类型的输入格式[^2]。 - **拓扑排序** 如果目标是找出有向无环图(DAG) 中各事件发生的先后次序,则可通过逆向构建森林的方法得到满足条件的结果序列——具体做法是在常规流程结束后额外维护一个全局变量 `order[]`, 将每轮结束后的当前结点编号压入其中最后反转数组输出即为所求。 - **连通性分析** 利用 DFS 探索网络内部连接情况有助于判断是否存在孤立部分或是计算强/弱联通区域数量等问题。对于大型社交平台而言尤其重要因为这关系着用户体验的好坏程度。 - **回溯算法** 当面临组合优化类难题比如数独游戏求解器设计时往往借助此类技术框架快速枚举潜在方案空间从而找到最优解或者可行解其中之一。 - **迷宫求解** 给定二维网格地图表示障碍物分布状况之后便能运用上述原理规划机器人行走路线确保能够顺利抵达终点而不碰壁撞墙。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值