深度优先搜索(DFS)完全解析:从原理到 Java 实战

深度优先搜索(DFS)完全解析:从原理到 Java 实战

@TOC
作为一名程序员,你是否遇到过需要在复杂的图结构中寻找路径、检测环,或者进行树遍历的问题?深度优先搜索(Depth-First Search, DFS)作为一种经典的图遍历算法,能够轻松应对这些场景。在 CSDN 社区中,技术文章的受欢迎程度往往取决于内容的实用性、代码的可读性以及图文结合的讲解方式。因此,本文将为你带来一篇深入浅出、图文并茂、代码详尽的 DFS 指南,涵盖原理、Java 实现、应用场景和实战示例,确保你不仅理解 DFS,还能立刻上手应用!


什么是深度优先搜索(DFS)?

深度优先搜索(DFS)是一种用于遍历或搜索树或图的算法。它的核心思想是:从一个起始节点开始,沿着一条路径尽可能深入地探索,直到无法继续前进时,再回溯到上一个分叉点,尝试其他路径。用一句话概括:DFS 是一种“先走到尽头再回头”的搜索策略

与广度优先搜索(BFS)“层层扩展”的方式不同,DFS 更像是一个勇敢的探险家,优先深入某一条路,直到碰壁才返回。这种特性使得 DFS 在某些问题(如路径查找、环检测)中特别高效。


DFS 的工作原理(图解)

为了让你直观理解 DFS 的执行过程,我们以一个简单的图为例:

图结构:0/ \1---2|3
  • 边表示:0-1, 0-2, 1-2, 2-3
  • DFS 从节点 0 开始
    1. 访问 0
    2. 从 0 进入 1,访问 1
    3. 从 1 进入 2,访问 2
    4. 从 2 进入 3,访问 3
    5. 3 没有未访问的邻居,回溯到 2
    6. 2 没有其他未访问邻居,回溯到 1
    7. 1 没有其他未访问邻居,回溯到 0
    8. 0 的所有邻居已访问,结束

访问顺序:0 -> 1 -> 2 -> 3

下图展示了 DFS 的过程(灰色表示已访问):

初始状态       访问 0        访问 1        访问 2        访问 30            0*           0*           0*           0*/ \          / \          / \          / \          / \1---2        1---2        1*--2        1*--2*       1*--2*|            |            |            |            |3            3            3            3*           3*

这种“一条路走到黑”的方式,正是 DFS 的精髓。


DFS 的应用场景

DFS 在实际开发中用途广泛,以下是几个典型场景:

  1. 路径查找:在迷宫或图中寻找从起点到终点的所有可能路径。
  2. 环检测:判断图中是否存在环,常用于依赖关系分析。
  3. 拓扑排序:对有向无环图(DAG)进行排序,例如任务调度。
  4. 连通性分析:在无向图中找出所有连通分量。
  5. 树遍历:实现二叉树的先序、中序、后序遍历。

用 Java 实现 DFS

在 Java 中,DFS 通常通过递归显式栈实现。这里我们以邻接表表示图,并用递归方式实现 DFS,因为它代码简洁且符合直觉。

完整代码示例

以下是一个基于邻接表的 DFS 实现,包含详细注释:

import java.util.*;public class DFSGraph {private int V; // 图的节点数private LinkedList<Integer>[] adj; // 邻接表表示图// 构造函数,初始化图public DFSGraph(int v) {V = v;adj = new LinkedList[v];for (int i = 0; i < v; i++) {adj[i] = new LinkedList<>(); // 为每个节点初始化邻接表}}// 添加边(无向图)public void addEdge(int v, int w) {adj[v].add(w); // v -> wadj[w].add(v); // w -> v(无向图需添加双向边)}// DFS 核心递归方法private void dfsUtil(int v, boolean[] visited) {visited[v] = true; // 标记当前节点为已访问System.out.print(v + " "); // 访问节点(这里打印)// 遍历当前节点的所有邻接节点for (int neighbor : adj[v]) {if (!visited[neighbor]) { // 如果邻接节点未被访问dfsUtil(neighbor, visited); // 递归访问}}}// DFS 入口方法public void DFS(int start) {boolean[] visited = new boolean[V]; // 记录访问状态dfsUtil(start, visited); // 从起始节点开始 DFS}// 测试代码public static void main(String[] args) {DFSGraph graph = new DFSGraph(5); // 创建一个 5 个节点的图// 添加边graph.addEdge(0, 1);graph.addEdge(0, 2);graph.addEdge(1, 3);graph.addEdge(2, 4);graph.addEdge(3, 4);System.out.println("从节点 0 开始的 DFS 遍历:");graph.DFS(0);}
}

运行结果

从节点 0 开始的 DFS 遍历:
0 1 3 4 2

代码详解

  1. 图的表示
    • 使用 LinkedList<Integer>[] adj 作为邻接表,adj[i] 存储节点 i 的所有邻接节点。
    • V 表示节点总数。
  2. 添加边
    • addEdge 方法为无向图添加双向边。
  3. DFS 实现
    • dfsUtil 是递归核心,标记并访问当前节点,然后递归处理未访问的邻接节点。
    • DFS 方法初始化 visited 数组并启动遍历。
  4. main 方法
    • 构建一个 5 节点图,添加边后从节点 0 开始 DFS。

DFS 的时间与空间复杂度

  • 时间复杂度:O(V + E)
    • V 是节点数,E 是边数,DFS 需要访问所有节点和边。
  • 空间复杂度:O(V)
    • 递归栈的深度最多为 V,加上 visited 数组的空间。

实战项目:迷宫求解

让我们通过一个迷宫问题展示 DFS 的应用。假设有一个 4x4 的迷宫,0 表示通路,1 表示墙,目标是从 (0,0) 到 (3,3) 找一条路径。

迷宫表示

0 1 0 0
0 1 0 1
0 0 0 0
1 1 0 0

Java 代码

public class MazeSolver {static int[][] maze = {{0, 1, 0, 0},{0, 1, 0, 1},{0, 0, 0, 0},{1, 1, 0, 0}};static int N = 4;static int[][] path = new int[N][N]; // 记录路径// 四个方向:上、右、下、左static int[] dx = {-1, 0, 1, 0};static int[] dy = {0, 1, 0, -1};public static boolean solveMaze(int x, int y) {// 到达终点 (3,3)if (x == N - 1 && y == N - 1) {path[x][y] = 1;return true;}// 检查当前位置是否合法if (isSafe(x, y)) {path[x][y] = 1; // 标记为路径的一部分// 尝试四个方向for (int i = 0; i < 4; i++) {int nextX = x + dx[i];int nextY = y + dy[i];if (solveMaze(nextX, nextY)) {return true;}}// 回溯:如果当前路径不通,撤销标记path[x][y] = 0;}return false;}// 检查坐标是否有效public static boolean isSafe(int x, int y) {return x >= 0 && x < N && y >= 0 && y < N && maze[x][y] == 0;}public static void main(String[] args) {if (solveMaze(0, 0)) {System.out.println("找到路径:");for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {System.out.print(path[i][j] + " ");}System.out.println();}} else {System.out.println("无解");}}
}

输出结果

找到路径:
1 0 0 0
1 0 0 0
1 1 1 1
0 0 0 1

解析

  • DFS 策略:从 (0,0) 开始,尝试四个方向(上、右、下、左),遇到墙或边界回溯。
  • 路径记录path 数组标记走过的位置,成功到达 (3,3) 时返回路径。

总结与互动

通过这篇文章,你应该已经掌握了 DFS 的原理、Java 实现以及实战应用。无论是图遍历还是迷宫求解,DFS 都展现了其简洁而强大的能力。为了加深理解,不妨试试以下问题:

  • 如何用 DFS 检测图中的环?

  • 如果用栈而非递归实现 DFS,会是什么样?

    欢迎在评论区分享你的代码或疑问,一起探讨 DFS 的更多玩法!如果觉得这篇博客对你有帮助,记得点赞和收藏哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/72962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【人工智能】如何理解transformer中的token?

如何理解transformer中的token? **一、Token在Transformer中的作用****二、文本分词的常见方法****1. 基于词典的分词&#xff08;Dictionary-based Tokenization&#xff09;****2. 子词分词&#xff08;Subword Tokenization&#xff09;****(1) WordPiece算法****(2) BPE&a…

AI风向标《AI与视频制作全攻略:从入门到精通实战课程》

课程信息 AI风向标《AI与视频制作全攻略&#xff1a;从入门到精通实战课程》,夸克网盘和百度网盘课程。 课程介绍 《AI与视频制作全攻略&#xff1a;从入门到精通实战课程》是一套全面融合AI技术与视频制作的实战课程&#xff0c;旨在帮助创作者从基础软件使用到高级视频剪辑…

mayfly-go开源的一站式 Web 管理平台

mayfly-go 是一款开源的一站式 Web 管理平台&#xff0c;旨在通过统一的界面简化 Linux 服务器、数据库&#xff08;如 MySQL、PostgreSQL、Redis、MongoDB 等&#xff09;的运维管理。以下从多个维度对其核心特性、技术架构、应用场景及生态进行详细解析&#xff1a; 一、核心…

车辆模型——运动学模型

文章目录 约束及系统移动机器人运动学模型&#xff08;Kinematic Model&#xff09;自行车模型含有加速度 a a a 的自行车模型系统偏差模型 在机器人的研究领域中&#xff0c;移动机器人的系统建模与分析是极为关键的基础环节&#xff0c;本文以非完整约束的轮式移动机器人为研…

go命令使用

查看配置信息 go env配置go国内源 export GO111MODULEon export GOPROXYhttps://goproxy.cn测试 go install github.com/jesseduffield/lazydockerlatesthttps://github.com/jesseduffield/lazydocker

Chrome-Edge-IDEA-Win 常用插件-工具包

Chrome-Edge-IDEA-Win 常用插件-工具包 Chrome-Edge-IDEA-Win 常用插件-工具包谷歌插件chropathJSONViewOctotree - GitHub code treeXPath Helper书签侧边栏篡改猴Print Edit WEEdge浏览器插件IDEA插件CodeGlance Pro 代码迷你缩放图插件Alibaba Cloud ToolkitAlibaba Java Co…

西门子V90伺服系统介绍

深入浅出地了解V90伺服驱动系统的核心特性和优势&#xff0c;掌握其自动优化功能&#xff0c;使设备获得更高的动态性能&#xff1b;同时&#xff0c;了解其自动抑制机械谐振频率的特性&#xff0c;有助于在实际应用中减少机械振动和噪音。 方便快捷地熟悉V90的使用方式。通过伺…

【FastGPT】利用知识库创建AI智能助手

【FastGPT】利用知识库创建AI智能助手 摘要创建知识库上传文档创建应用准备提示词准备开场白关联知识库AI回答效果 摘要 关于FastGPT的部署&#xff0c;官方提供了docker-compose方式的部署文档&#xff0c;如果使用的是podman和podman-compose的同学&#xff0c;可以参考这篇…

最新!Ubuntu Docker 安装教程

源自: AINLPer&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2025-3-1 更多&#xff1a;>>>>大模型/AIGC、学术前沿的知识分享&#xff01; 看到很多部署大模型的时候&#xff0c;都是基于docker安装部署的。…

html5炫酷3D立体文字效果实现详解

炫酷3D立体文字效果实现详解 这里写目录标题 炫酷3D立体文字效果实现详解项目概述技术实现要点1. 基础布局设置2. 动态背景效果3. 文字渐变效果4. 立体阴影效果5. 悬浮动画效果 技术难点及解决方案1. 文字渐变动画2. 立体阴影效果3. 性能优化 浏览器兼容性总结 项目概述 在这个…

电脑如何设置几分钟后自动关机

摘要&#xff1a;本文提供Windows、macOS和Linux系统设置定时自动关机的详细方法。 目录 一、Windows系统设置方法 设置定时关机 取消关机计划 二、macOS系统设置方法 设置定时关机取消关机计划 三、Linux系统设置方法 设置定时关机 取消关机计划 四、注意事项五、扩展&#x…

Android音视频多媒体开源库基础大全

从事音视频开发工作&#xff0c;需要了解哪些常见的开源库&#xff0c;从应用到底软系统&#xff0c;整理了九大类&#xff0c;这里一次帮你总结完。 包含了应用层的MediaRecorder、surfaceView&#xff0c;以及常见音视频处理库FFmpeg和OpenCV&#xff0c;还有视频渲染和音频…

若依前端框架增删改查

1.下拉列表根据数据库加载 这个是用来查询框 绑定了 change 事件来处理站点选择变化后的查询逻辑。 <el-form-item label"站点选择" prop"stationId" v-has-permi"[ch:m:y]"><el-select v-model"queryParams.stationId" pl…

Java 第十一章 GUI编程(3)

目录 内部类 内部类定义 内部类的特点 匿名内部类 格式&#xff1a; 内部类的意义 实例 内部类 ● 把类定义在另一个类的内部&#xff0c;该类就被称为内部类。 ● 如果在类 Outer 的内部再定义一个类 Inner&#xff0c;此时类 Inner 就称为内部类 &#xff08;或称为嵌…

Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测

Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测 目录 Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预…

3DMAX曲线生成器插件CurveGenerator使用方法

1. 脚本功能简介 3DMAX曲线生成器插件CurveGenerator是一个用于 3ds Max 的样条线生成工具&#xff0c;用户可以通过简单的UI界面输入参数&#xff0c;快速生成多条样条线。每条样条线的高度值随机生成&#xff0c;且可以自定义以下参数&#xff1a; 顶点数量&#xff1a;每条…

LiteratureReading:[2023] GPT-4: Technical Report

文章目录 一、文献简明&#xff08;zero&#xff09;二、快速预览&#xff08;first&#xff09;1、标题分析2、作者介绍3、引用数4、摘要分析&#xff08;1&#xff09;翻译&#xff08;2&#xff09;分析 5、总结分析&#xff08;1&#xff09;翻译&#xff08;2&#xff09;…

vm_pwn入门 -- [GHCTF 2025]my_vm

先看基本逻辑 int __fastcall main(int argc, const char **argv, const char **envp) {unsigned __int16 IP; // [rspCh] [rbp-14h] BYREFunsigned __int16 SP; // [rspEh] [rbp-12h] BYREFunsigned __int16 cmd_count; // [rsp10h] [rbp-10h] BYREFunsigned __int16 i; // [r…

CA 机构如何防止中间人攻击

在现代互联网中&#xff0c;中间人攻击&#xff08;Man-in-the-Middle Attack&#xff0c;简称 MITM&#xff09;是一种常见的网络攻击方式&#xff0c;攻击者通过拦截和篡改通信双方的信息&#xff0c;进而窃取敏感数据或执行恶意操作。为了防止中间人攻击&#xff0c;证书颁发…

Elasticsearch快速上手与深度进阶:一站式实战教程

目录 1. Elasticsearch 简介 2. 安装与启动 方式 1&#xff1a;Docker 快速安装&#xff08;推荐&#xff09; 方式 2&#xff1a;手动安装 3. 基础操作 3.1 创建索引 3.2 插入文档 3.3 查询文档 3.4 更新文档 3.5 删除文档 4. 高级查询 4.1 布尔查询 4.2 范围查询…