java dfs 详解

embedded/2025/2/12 15:20:36/

深度优先搜索(DFS)详解

深度优先搜索(DFS, Depth-First Search)是一种常见的图或树的搜索算法,它尝试从起点开始,一直沿着一个方向搜索到尽可能深的位置,然后回溯,尝试其他路径,直到找到目标或访问所有可能的节点。

在 Java 中,DFS 通常以递归或显式栈的方式实现。


DFS 的基本特性

  1. 搜索方式

    • 沿着一个分支一直深入,直到无法继续,再回溯到上一级,探索其他分支。
    • 访问顺序是“先深后广”。
  2. 实现方式

    • 递归实现:利用系统栈存储递归状态。
    • 显式栈实现:通过手动管理栈,模拟递归过程。
  3. 适用场景

    • 图或树的遍历(如查找路径、连通性问题)。
    • 搜索问题(如迷宫、数独)。
    • 解决组合问题(如全排列、子集生成)。
  4. 时间复杂度

    • 一般为 O ( V + E ) O(V + E) O(V+E),其中 V V V 是节点数, E E E 是边数。
  5. 空间复杂度

    • 递归实现:受递归深度限制,为 O ( H ) O(H) O(H),其中 H H H 是递归深度。
    • 显式栈实现:栈的最大深度为 O ( H ) O(H) O(H)

DFS 的核心步骤

  1. 访问当前节点
  2. 标记节点为已访问(如果是图)。
  3. 递归或循环访问邻接节点

DFS 的实现(递归)

示例1:遍历一棵二叉树
java">class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val = val;}
}public class DFSExample {public void dfs(TreeNode root) {if (root == null) {return;}System.out.println(root.val); // 访问当前节点dfs(root.left); // 遍历左子树dfs(root.right); // 遍历右子树}public static void main(String[] args) {TreeNode root = new TreeNode(1);root.left = new TreeNode(2);root.right = new TreeNode(3);root.left.left = new TreeNode(4);root.left.right = new TreeNode(5);new DFSExample().dfs(root); // 输出:1, 2, 4, 5, 3}
}

示例2:遍历一个无向图
java">import java.util.*;public class GraphDFS {private Map<Integer, List<Integer>> graph = new HashMap<>();private Set<Integer> visited = new HashSet<>();// 添加边public void addEdge(int u, int v) {graph.putIfAbsent(u, new ArrayList<>());graph.putIfAbsent(v, new ArrayList<>());graph.get(u).add(v);graph.get(v).add(u); // 无向图,双向边}// DFS 方法public void dfs(int node) {if (visited.contains(node)) {return;}visited.add(node);System.out.println(node); // 访问节点for (int neighbor : graph.get(node)) {dfs(neighbor);}}public static void main(String[] args) {GraphDFS g = new GraphDFS();g.addEdge(1, 2);g.addEdge(1, 3);g.addEdge(2, 4);g.addEdge(2, 5);g.addEdge(3, 6);g.dfs(1); // 输出:1, 2, 4, 5, 3, 6(可能顺序不同)}
}

DFS 的实现(显式栈)

示例3:栈实现图的 DFS
java">import java.util.*;public class StackDFS {private Map<Integer, List<Integer>> graph = new HashMap<>();// 添加边public void addEdge(int u, int v) {graph.putIfAbsent(u, new ArrayList<>());graph.putIfAbsent(v, new ArrayList<>());graph.get(u).add(v);graph.get(v).add(u); // 无向图,双向边}// 栈实现 DFSpublic void dfs(int start) {Stack<Integer> stack = new Stack<>();Set<Integer> visited = new HashSet<>();stack.push(start);while (!stack.isEmpty()) {int node = stack.pop();if (!visited.contains(node)) {visited.add(node);System.out.println(node); // 访问节点// 将邻居节点压入栈for (int neighbor : graph.getOrDefault(node, new ArrayList<>())) {if (!visited.contains(neighbor)) {stack.push(neighbor);}}}}}public static void main(String[] args) {StackDFS g = new StackDFS();g.addEdge(1, 2);g.addEdge(1, 3);g.addEdge(2, 4);g.addEdge(2, 5);g.addEdge(3, 6);g.dfs(1); // 输出:1, 3, 6, 2, 5, 4(可能顺序不同)}
}

DFS 的应用场景

1. 全排列与组合
java">import java.util.*;public class Permutations {public void dfs(List<Integer> nums, List<Integer> path, boolean[] visited) {if (path.size() == nums.size()) {System.out.println(path); // 输出当前排列return;}for (int i = 0; i < nums.size(); i++) {if (visited[i]) {continue;}visited[i] = true;path.add(nums.get(i));dfs(nums, path, visited);path.remove(path.size() - 1); // 回溯visited[i] = false;}}public static void main(String[] args) {List<Integer> nums = Arrays.asList(1, 2, 3);new Permutations().dfs(nums, new ArrayList<>(), new boolean[nums.size()]);// 输出:[1, 2, 3], [1, 3, 2], [2, 1, 3], ...}
}

2. 寻找岛屿数量(二维矩阵中的连通分量)
java">public class NumberOfIslands {private void dfs(char[][] grid, int i, int j) {if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == '0') {return;}grid[i][j] = '0'; // 标记已访问dfs(grid, i - 1, j); // 上dfs(grid, i + 1, j); // 下dfs(grid, i, j - 1); // 左dfs(grid, i, j + 1); // 右}public int numIslands(char[][] grid) {int count = 0;for (int i = 0; i < grid.length; i++) {for (int j = 0; j < grid[0].length; j++) {if (grid[i][j] == '1') {count++;dfs(grid, i, j);}}}return count;}public static void main(String[] args) {char[][] grid = {{'1', '1', '0', '0'},{'1', '0', '0', '1'},{'0', '0', '1', '1'}};System.out.println(new NumberOfIslands().numIslands(grid)); // 输出:3}
}

DFS 的优缺点

优点
  1. 内存占用低(如果递归深度不高)。
  2. 适合用于搜索特定路径和解决分支问题。
  3. 实现简单,代码结构清晰。
缺点
  1. 对递归深度敏感,容易导致栈溢出。
  2. 无法保证最短路径的优先级(对于路径问题,应使用 BFS)。

总结

DFS 是一种强大的算法,适用于多种场景,包括树和图的遍历、路径查找、连通分量等问题。通过递归和显式栈两种方式实现,DFS 既直观又灵活,但需要注意递归深度和路径记录问题。在复杂应用中,可结合回溯、记忆化等技术提升效率。


http://www.ppmy.cn/embedded/140568.html

相关文章

Chrome离线安装包下载

1、问Chrome的官网&#xff1a;https://www.google.cn/chrome/ 直接下载的是在线安装包&#xff0c;安装需要联网。 2、如果需要在无法联网的设备上安装Chrome&#xff0c;需要在上面的地址后面加上?standalone1。 Chrome离线安装包下载地址&#xff1a;https://www.google.c…

Python入门(16)--自动化测试教程

自动化测试教程 &#x1f50d; 1. 单元测试编写 ✅ 1.1 unittest框架介绍 Python的unittest框架提供了编写和运行测试的完整工具集&#xff1a; import unittestclass TestStringMethods(unittest.TestCase):def setUp(self):"""测试前的准备工作"&quo…

【eNSP】ISIS动态路由协议实验

和OSPF一样&#xff0c;IS-IS也是一种基于链路状态并使用最短路径优先算法进行路由计算的一种IGP协议。IS-IS最初是国际化标准组织ISO为它的无连接网络协议CLNP设计的一种动态路由协议。 为了提供对IP的路由支持&#xff0c;IETF在RFC1195中对IS-IS进行了扩充和修改&#xff0c…

大规模历史数据如何管理?(附解决方法)

随着企业业务规模拓展&#xff0c;数据呈爆炸性增长&#xff0c;面对不断增长的数据&#xff0c;显然传统的数据存储和管理方式已经无法满足企业对大规模数据的要求。那么如何有效和存储大规模的历史数据&#xff0c;以满足企业数据查询和分析的需求&#xff1f; 一、数据库系…

UE5 Switch Has Authority 节点

在 Unreal Engine 5 (UE5) 中&#xff0c;Switch Has Authority 节点用于在蓝图中根据当前操作是否具有 Authority 来切换逻辑。这个节点常用于处理 网络同步 和 多玩家 环境中的客户端与服务器之间的不同逻辑。具体而言&#xff0c;它允许你根据当前执行代码的实体&#xff08…

Redis(非关系型数据库)详细介绍

Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、高性能的、基于内存的数据结构存储系统。它通常被用于缓存、消息队列、会话存储等场景。由于其强大的功能和卓越的性能&#xff0c;Redis 被广泛应用于现代互联网企业中&#xff0c;是大多数分布式系统中不…

提供html2canvas+jsPDF将HTML页面以A4纸方式导出为PDF后,内容分页时存在截断的解决思路

前言 最近公司有个系统要做一个质量报告导出为PDF的需求&#xff0c;这个报表的内容是固定格式&#xff0c;但是不固定内容多少的&#xff0c;网上找了很多资料&#xff0c;没有很好的解决我的问题&#xff0c;pdfmakde、还有html2CanvasjsPDF以及Puppeteer无头浏览器的方案都不…

【qt版本概述】

Question qt版本概述 Answer Qt 是一个跨平台的应用程序开发框架&#xff0c;广泛用于开发GUI程序和嵌入式系统。以下是几个主要版本的概述&#xff1a; Qt 4.x: 主要引入了新的图形视图框架&#xff0c;增强了对3D图形的支持。改进了模型/视图架构&#xff0c;使得数据与视…