算法系列之搜索算法-深度优先搜索DFS

简介: 深度优先搜索和广度优先搜索一样,都是对图进行搜索的算法,目的也都是从起点开始搜索,直到到达顶点。深度优先搜索会沿着一条路径不断的往下搜索,直到不能够在继续为止,然后在折返,开始搜索下一条候补路径。

8483ff80-2b40-43e3-b83f-139a7ee7b337.jpg

随着每年"金三银四"招聘季的到来,许多求职者开始积极备战面试。在众多面试环节中,机试往往是不可或缺的一环,而算法能力更是机试考核的重点。为此,我们特别推出算法系列文章,帮助大家系统复习算法知识。今天,我们将首先探讨搜索算法中的重要内容——深度度优先搜索(DFS)。

图的介绍

图(Graph)是一种非线性的数据结构,由顶点(Vertex)和边(Edge)组成。如下图所示

_20250219193211.jpg
分类如下:

  • 无向图(Undirected Graph):边没有方向,表示双向关系。

  • 有向图(Directed Graph):边有方向,表示单向关系。

  • 加权图(Weighted Graph):边带有权重。

  • 无权图(Unweighted Graph):边没有权重。

深度优先搜索(DFS, Depth-First Search)

深度优先搜索和广度优先搜索一样,都是对图进行搜索的算法,目的也都是从起点开始搜索,直到到达顶点。深度优先搜索会沿着一条路径不断的往下搜索,直到不能够在继续为止,然后在折返,开始搜索下一条候补路径。

DFS 可以借助于栈或者来实现。栈具有”后进先出(LIFO)”特性,可以是有栈或者递归来实现遍历。其实现步骤如下:

  1. 访问节点:从起始节点开始,访问当前节点。

  2. 递归

  • 递归访问邻居:对于当前节点的每一个未访问过的邻居节点,递归地调用 DFS。

  • 回溯:当没有未访问的邻居时,回溯到上一个节点,继续搜索其他路径。

  • 使用 Stack 来模拟递归过程,每次从栈中弹出一个节点并访问它,然后将未访问的邻居节点压入栈中。

示例代码如下:

/**
 * 深度优先搜索示例
 */
public class DFSExample {
   
    // 定义图的节点类
    static class Node {
   
        int value;
        List< Node> neighbors;

        public Node(int value) {
   
            this.value = value;
            this.neighbors = new ArrayList<>();
        }

        // 添加邻接节点
        public void addNeighbor( Node neighbor) {
   
            this.neighbors.add(neighbor);
        }
    }

    /**
     * 方式一 :栈实现
     * dfs 函数
     * @param startNode
     */
    public static void dfs( Node startNode) {
   

        if(startNode == null ) return;
        // 使用队列存储待访问的节点
        Stack<Node> stack = new Stack<>();

        // 使用HashSet记录已访问的节点
        Set<Node> visited = new HashSet<>();
        // 将起点加入栈并标记为已访问
        stack.push(startNode);
        visited.add(startNode);
        // 遍历栈
        while (!stack.isEmpty()){
   
             Node currentNode = stack.pop();
            System.out.println(currentNode.value);
            // 遍历当前节点的所有邻接节点
            for (Node neighbor : currentNode.neighbors) {
   
                // 如果邻接节点未被访问,则加入栈并标记为已访问
                if (!visited.contains(neighbor)) {
   
                    stack.add(neighbor);
                    visited.add(neighbor);
                }
            }
        }

    }
    //方式二:递归实现
    public static void sec( Node currentNode,Set<Node> visited) {
   

        // 标记当前节点为已访问
        visited.add(currentNode);
        System.out.println(currentNode.value);
        // 递归访问所有未访问的邻居节点
        for (Node neighbor : currentNode.neighbors) {
   
            if (!visited.contains(neighbor))
                sec(neighbor, visited);
        }
    }

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
   
        Node node1 = new  Node(1);
        Node node2 = new  Node(2);
        Node node3 = new  Node(3);
        Node node4 = new  Node(4);
        Node node5 = new  Node(5);
        Node node6 = new  Node(6);


        node1.addNeighbor(node2);
        node1.addNeighbor(node3);
        node1.addNeighbor(node5);

        node2.addNeighbor(node1);
        node2.addNeighbor(node3);
        node2.addNeighbor(node5);

        node3.addNeighbor(node1);
        node3.addNeighbor(node2);
        node3.addNeighbor(node4);
        node3.addNeighbor(node6);

        node4.addNeighbor(node3);
        node4.addNeighbor(node6);

        node5.addNeighbor(node2);
        node5.addNeighbor(node6);

        node6.addNeighbor(node3);
        node6.addNeighbor(node4);
        node6.addNeighbor(node5);
        //栈实现
        dfs(node1);
        System.out.println("+++++++++递归实现++++++++++++");
        //递归实现
        Set< Node> visited = new HashSet<>();
        sec(node1,visited);
    }
}

DFS的特点

  • 时间复杂度:O(V+E)
  • 空间复杂度:O(V)
  • 适用场景:连通性检测、路径查找、迷宫求解

DFS 示例题

以下列举了一些机试题

广播服务器

题目描述:给定一个大小为 N×N 的二维矩阵 matrix,表示 N 个服务器之间的连接情况。matrix[i][j] = 1 表示服务器i 和 j 直接连接,matrix[i][j] = 0 表示不直接连接。计算初始需要给几台服务器广播,才能使每个服务器都收到广播。
输入:N 行,每行 N 个数字(0 或 1),表示 N×N 的二维矩阵。
输出:需要广播的服务器的数量。

示例一:
输入:
1 0 0
0 1 0
0 0 1
输出:3

示例二:
输入:
1 1
1 1
输出:1

public class DFSServer {
   

    public static void main(String[] args) {
   
        Scanner scanner = new Scanner(System.in);
        String[] firstLine = scanner.nextLine().split(" ");
        int n = firstLine.length;
        //初始化二维数组
        int[][] matrix = new int[n][n];
        for (int i = 0; i < n; i++) {
   
            String[] line = i==0 ? firstLine:scanner.nextLine().split(" ");
            for (int j = 0; j < n; j++) {
   
                matrix[i][j] = Integer.parseInt(line[j]);
            }
        }
        //初始化标记数组
        int[] check = new int[n];
        //需要广播的服务器的数量
        int ans = 0;
        Stack<Integer> stack = new Stack<>();
        // 遍历每个服务器
        for (int i = 0; i < n; i++) {
   
            // 如果服务器 i 没有被访问过,就以它为起点进行 DFS
            if (check[i] == 0) {
   
                ans++; // 新的连通分量,计数器加 1
                stack.add(i); // 将当前服务器加入栈
                check[i] = 1; // 标记为已访问

                // 开始 DFS
                while (!stack.isEmpty()) {
   
                    // 弹出当前服务器
                    int cur = stack.pop();
                    // 遍历所有服务器,找到与 cur 相连且未访问的服务器
                    for (int j = 0; j < n; j++) {
   
                        if (j != cur && matrix[cur][j] == 1 && check[j] == 0) {
   
                            // 将未访问的服务器加入栈
                            stack.push(j);
                            // 将未访问的服务器加入栈
                            check[j] = 1;
                        }
                    }
                }
            }
        }

        System.out.println(ans);

    }
}

总结

DFS 是一种非常重要的图遍历算法,适用于许多场景。递归实现简单直观,但可能会受到栈深度的限制;迭代实现则避免了这个问题,但代码稍复杂一些。根据具体需求选择合适的实现方式。

目录
相关文章
|
17天前
|
存储 算法 调度
基于和声搜索优化算法的机器工作调度matlab仿真,输出甘特图
本程序基于和声搜索优化算法(Harmony Search, HS),实现机器工作调度的MATLAB仿真,输出甘特图展示调度结果。算法通过模拟音乐家即兴演奏寻找最佳和声的过程,优化任务在不同机器上的执行顺序,以最小化完成时间和最大化资源利用率为目标。程序适用于MATLAB 2022A版本,运行后无水印。核心参数包括和声记忆大小(HMS)等,适应度函数用于建模优化目标。附带完整代码与运行结果展示。
|
13天前
|
人工智能 自然语言处理 算法
阿里云 AI 搜索开放平台:从算法到业务——AI 搜索驱动企业智能化升级
本文介绍了阿里云 AI 搜索开放平台的技术的特点及其在各行业的应用。
104 3
|
15天前
|
人工智能 运维 算法
基于 C# 深度优先搜索算法的局域网集中管理软件技术剖析
现代化办公环境中,局域网集中管理软件是保障企业网络高效运行、实现资源合理分配以及强化信息安全管控的核心工具。此类软件需应对复杂的网络拓扑结构、海量的设备信息及多样化的用户操作,而数据结构与算法正是支撑其强大功能的基石。本文将深入剖析深度优先搜索(Depth-First Search,DFS)算法,并结合 C# 语言特性,详细阐述其在局域网集中管理软件中的应用与实现。
48 3
|
1月前
|
监控 算法 安全
基于 PHP 语言深度优先搜索算法的局域网网络监控软件研究
在当下数字化时代,局域网作为企业与机构内部信息交互的核心载体,其稳定性与安全性备受关注。局域网网络监控软件随之兴起,成为保障网络正常运转的关键工具。此类软件的高效运行依托于多种数据结构与算法,本文将聚焦深度优先搜索(DFS)算法,探究其在局域网网络监控软件中的应用,并借助 PHP 语言代码示例予以详细阐释。
39 1
|
2月前
|
运维 监控 JavaScript
内网网管软件中基于 Node.js 的深度优先搜索算法剖析
内网网管软件在企业网络中不可或缺,涵盖设备管理、流量监控和安全防护。本文基于Node.js实现深度优先搜索(DFS)算法,解析其在网络拓扑遍历中的应用。通过DFS,可高效获取内网设备连接关系,助力故障排查与网络规划。代码示例展示了图结构的构建及DFS的具体实现,为内网管理提供技术支持。
58 11
|
1月前
|
算法 安全 Java
算法系列之深度优先搜索寻找妖怪和尚过河问题的所有方式
在算法学习中,深度优先搜索(DFS)是一种常用的图搜索算法,通过递归或栈实现,适合路径搜索、连通性、拓扑排序、回溯、生成、环路检测、强连通分量和可达性等问题。本文将介绍如何利用深度优先搜索解决“妖怪和尚过河问题”的所有方式。
87 26
算法系列之深度优先搜索寻找妖怪和尚过河问题的所有方式
|
28天前
|
监控 算法 JavaScript
企业用网络监控软件中的 Node.js 深度优先搜索算法剖析
在数字化办公盛行的当下,企业对网络监控的需求呈显著增长态势。企业级网络监控软件作为维护网络安全、提高办公效率的关键工具,其重要性不言而喻。此类软件需要高效处理复杂的网络拓扑结构与海量网络数据,而算法与数据结构则构成了其核心支撑。本文将深入剖析深度优先搜索(DFS)算法在企业级网络监控软件中的应用,并通过 Node.js 代码示例进行详细阐释。
38 2
|
1月前
|
存储 算法 JavaScript
基于 Node.js 深度优先搜索算法的上网监管软件研究
在数字化时代,网络环境呈现出高度的复杂性与动态性,上网监管软件在维护网络秩序与安全方面的重要性与日俱增。此类软件依托各类数据结构与算法,实现对网络活动的精准监测与高效管理。本文将深度聚焦于深度优先搜索(DFS)算法,并结合 Node.js 编程语言,深入剖析其在上网监管软件中的应用机制与效能。
34 6
|
2月前
|
存储 算法
算法系列之搜索算法-广度优先搜索BFS
广度优先搜索(BFS)是一种非常强大的算法,特别适用于解决最短路径、层次遍历和连通性问题。在面试中,掌握BFS的基本实现和应用场景,能够帮助你高效解决许多与图或树相关的问题。
106 1
算法系列之搜索算法-广度优先搜索BFS
|
10天前
|
算法 安全 数据安全/隐私保护
基于AES的遥感图像加密算法matlab仿真
本程序基于MATLAB 2022a实现,采用AES算法对遥感图像进行加密与解密。主要步骤包括:将彩色图像灰度化并重置大小为256×256像素,通过AES的字节替换、行移位、列混合及轮密钥加等操作完成加密,随后进行解密并验证图像质量(如PSNR值)。实验结果展示了原图、加密图和解密图,分析了图像直方图、相关性及熵的变化,确保加密安全性与解密后图像质量。该方法适用于保护遥感图像中的敏感信息,在军事、环境监测等领域具有重要应用价值。