【算法】深度优先搜索 (DFS)

news2024/11/19 5:56:38

目录

  • 1.概述
  • 2.代码实现
  • 3.应用

1.概述

(1)深度优先遍历 (Depth First Search, DFS),是图的搜索算法之一,本质其实就是一个递归的过程,它就像是一棵树的前序遍历

(2)DFS 从图中某个顶点 start 出发,访问此顶点,然后从 start 的未被访问的邻接点出发深度优先遍历图,直至图中所有和 start 有路径相通的顶点都被访问到。事实上这里讲到的是连通图,对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

2.代码实现

(1)当使用邻接矩阵来表示图时,其代码实现如下:

class Solution {
	/*
    	adjMatrix 为邻接矩阵,adjMatrix[i][j] = 0 表示节点 i 和 j 之间没有边直接相连
    	start 为遍历的起点
	*/
    public void dfs(int[][] adjMatrix, int start) {
        // n 表示图中的节点数量,节点编号为 0 ~ n - 1
        int n = adjMatrix.length;
        //定义 visited 数组,防止对节点进行重复遍历
        boolean[] visited = new boolean[n];
        if (start < 0 || start > n - 1) {
            System.out.println("起点编号应为 [0, " + (n - 1) + "] 之间的整数!");
            return;
        }
        dfsTraverse(adjMatrix, start, visited);
    }
    
    private void dfsTraverse(int[][] adjMatrix, int node, boolean[] visited) {
        //标记当前访问到的节点
        visited[node] = true;
        System.out.print(node + " ");
        for (int i = 0; i < adjMatrix.length; i++) {
            //遍历邻接矩阵中第 node 行
            if (adjMatrix[node][i] != 0 && !visited[i]) {
                dfsTraverse(adjMatrix, i, visited);
            }
        }
    }
}

(2)当使用邻接表来表示图时,其代码实现如下:

class Solution {
 	/*
    	adjList 为邻接表,adjList[i] 中存储与节点 i 相邻的节点
    	start 为遍历的起点
	*/
    public void dfs(List<Integer>[] adjList, int start) {
        // n 表示图中的节点数量,节点编号为 0 ~ n - 1
        int n = adjList.length;
        //定义 visited 数组,防止对节点进行重复遍历
        boolean[] visited = new boolean[n];
        if (start < 0 || start > n - 1) {
            System.out.println("起点编号应为 [0, " + (n - 1) + "] 之间的整数!");
            return;
        }
        dfsTraverse(adjList, start, visited);
    }
    
    private void dfsTraverse(List<Integer>[] adjList, int node, boolean[] visited) {
        //标记当前访问到的节点
        visited[node] = true;
        System.out.print(node + " ");
        for (int nextNode : adjList[node]) {
            while (!visited[nextNode]) {
                dfsTraverse(adjList, nextNode, visited);
            }
        }
    }
}

(3)下面以图 G 为例来说明:

在这里插入图片描述

① 构造邻接矩阵:

int[][] adjMatrix = {
	        {0, 1, 1, 0, 1},
	        {1, 0, 0, 1, 1},
	        {1, 0, 0, 0, 1},
	        {0, 1, 0, 0, 1},
	        {1, 1, 1, 1, 0}
	};

② 构造邻接表:

int n = 5;
List<Integer>[] adjList = new ArrayList[n];
for (int i = 0; i < n; i++) {
    adjList[i] = new ArrayList<>();
}
adjList[0].add(1);
adjList[0].add(2);
adjList[0].add(4);
adjList[1].add(0);
adjList[1].add(3);
adjList[1].add(4);
adjList[2].add(0);
adjList[2].add(4);
adjList[3].add(1);
adjList[3].add(4);
adjList[4].add(0);
adjList[4].add(1);

如果 start = 2,那么遍历的节点依次为:

2 0 1 3 4

遍历过程如下图所示:
在这里插入图片描述

(3)DFS算法是一个递归算法,需要借助一个递归工作栈,故其空间复杂度为O(|V|)。遍历图的过程实质上是对每个顶点查找其邻接点的过程,其耗费的时间取决于所用的存储结构:

  • 邻接矩阵来表示时,查找每个顶点的邻接点所需的时间为 O(|V|),故总的时间复杂度为 O(|V|2)
  • 邻接表来表示时,查找所有顶点的邻接点所需的时间为 O(|E|),访问顶点所需的时间为 O(|V|),此时,总的时间复杂度为 O(IV| + |E|)。

3.应用

(1)除了对图进行遍历以外,DFS 在求解图中是否存在路径中也有应用。

(2)以 LeetCode 中的1971.寻找图中是否存在路径这题为例:

在这里插入图片描述

分析如下:
① 根据题目所给的数组 edges 来构造邻接表 adj;
② 开始进行 DFS:
1)从起点 source 开始遍历并进行深度搜索,在搜索的过程中每访问到一个节点 v,如果该节点就是终点 destination,那么直接返回 true;
2)否则将其标记为已访问状态,并且继续递归访问与 v 相邻的下一个未访问节点 next,如果此时 next 与 destination 存在有效路径,那么说明起点 source 到终点 destination 之间也存在有效路径,此时直接返回 true 即可;
3)当访问完所有邻接节点后仍然没有访问到 destination,则返回 false。

具体代码实现如下:

public class Solution {
    public boolean validPath(int n, int[][] edges, int source, int destination) {
        //构造邻接表
        List<Integer>[] adj = new List[n];
        for (int i = 0; i < n; i++) {
            adj[i] = new ArrayList<>();
        }
        for (int[] edge : edges) {
            int x = edge[0];
            int y = edge[1];
            adj[x].add(y);
            adj[y].add(x);
        }
        // visited 用于标记每个节点是否已经被访问过
        boolean[] visited = new boolean[n];
        return dfs(source, destination, adj, visited);
    }
    
    //判断从起点 source 到终点 destination 是否存在有效路径,如果有则返回 true,否则返回 true
    private boolean dfs(int source, int destination, List<Integer>[] adj, boolean[] visited) {
        if (source == destination) {
            return true;
        } else {
            //标记当前访问到的节点
            visited[source] = true;
            for (int next : adj[source]) {
                while (!visited[next] && dfs(next, destination, adj, visited)) {
                    return true;
                }
            }
            return false;
        }
    }
}

(3)大家可以去 LeetCode 上找相关的 DFS 的题目来练习,或者也可以直接查看LeetCode算法刷题目录(Java)这篇文章中的 DFS 章节。如果大家发现文章中的错误之处,可在评论区中指出。

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

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

相关文章

游戏如何解决注入挂难题

游戏黑灰产的攻击角度除了常见的内存修改、模拟点击、破解等作弊手段&#xff0c;还有门槛相对较高的「专用插件类」。 专用插件类外挂是指针对特定游戏定制的外挂&#xff0c;其在实现方式上&#xff0c;类似插件&#xff0c;也称“定制挂”、“注入挂”。 游戏面临多样化的安…

招聘老师的最佳实践及工作交流坊

香港 — 如果你在招聘外籍老师途中遇到极大的挑战&#xff0c;你并不是孤独的。由于新冠肺炎的限制及对于外籍老师的需求增加&#xff0c;招聘及留住高质量的外籍老师对于学校来说已经越来越困难。在疫情之下&#xff0c; 许多学校展现了非凡的韧性来确保他们的教学质量及学习供…

【HCIA-openEuler】实验手册—04【openEuler用户及权限管理】

文章目录一、实验介绍1、关于本实验2、实验目的二、实验任务配置1、配置步骤&#xff08;1&#xff09;用户和用户组的管理步骤1&#xff1a;who命令是显示目前登录系统的用户信息步骤2&#xff1a;id命令用于显示用户的ID&#xff0c;以及所属群组的ID步骤3&#xff1a;以root…

Vivado 错误代码 [Place 30-574]解决思路

问题描述 最近利用手头的开发板作UDP通信的设计。准备生成比特流时&#xff0c;出现这个错误&#xff1a; 具体信息&#xff1a; [Place 30-574] Poor placement for routing between an IO pin and BUFG. If this sub optimal condition is acceptable for this design, you …

Java(105):Java通过键盘(Scanner)输入数据

Java通过键盘(Scanner)输入数据 在Java中&#xff0c;我们可以使用Scanner 类来获取用户的输入。 Java 中添加了java.util.Scanner类&#xff0c;这是一个用于扫描输入文本的新的实用程序。相比于其他获取用户输入的方式&#xff0c;Scanner是非常方便的。 如果使用Scanner&…

如何理解鲁棒性?为什么robustness会翻译为鲁棒性?

鲁棒性&#xff0c;英文为Robustness&#xff08;承受故障和干扰的能力&#xff09;&#xff0c;是许多复杂系统&#xff08;包括复杂网络&#xff09;的关键属性。复杂网络的鲁棒性研究对许多领域都非常重要。本文着重介绍了鲁棒性的基本定义、命名起源、分类区别、提升方法和…

一图读懂mybatis插件plugin原理

插件是用来改变或者扩展mybatis的原有的功能&#xff0c;mybaits的插件就是通过继承Interceptor拦截器实现的;mybatis中能使用插件进行拦截的 可以进行拦截的 接口和方法如下: Executor (update、query 、 flushStatment 、 commit 、 rollback 、 getTransaction 、 close 、…

机试_1_暴力求解_习题

暴力求解——习题 学习完第一章–暴力求解之后&#xff0c;当然要做相应地练习啦~ https://blog.csdn.net/Window_mouse/article/details/128632426 注&#xff1a;上述习题都可以在牛客进行测试。 例如&#xff0c;第9题链接&#xff1a;xxx定律_牛客题霸_牛客网 (nowcode…

近几年美赛B题分析

美赛B题概述&#xff1a; 美赛赛题类型美国大学生数学建模竞赛目前分为两种类型&#xff0c;MCM&#xff08;Mathematical Contest In Modeling&#xff09;和 ICM&#xff08;Interdisciplinary Contest In Modeling)&#xff0c;两种类型竞赛采用统一标准进行&#xff0c;竞…

金融风控04

特征工程 Filter 1&#xff09;移除低方差特征 假设某特征的特征值只有0和1&#xff0c;并且在所有输入样本中&#xff0c;95%的实例的该特征取值都是1&#xff0c;那就可以认为这个特征作用不大。如果100%都是1&#xff0c;那这个特征就没意义了。当特征值都是离散型变量的…

攻克强化学习技术难题记录

一共经过了5次迭代。 第1次迭代的设计思路&#xff1a; 强化学习demo游戏“cartpole”重述 游戏目标&#xff1a;向左/右移动小车cart&#xff0c;保证杆pole始终在小车上方&#xff0c;是大多数强化学习入门教材都会介绍的一个经典案例。 强化学习要素分析&#xff1a; 智…

【项目实战】Nacos下发路由配置实现Spring Cloud Gateway的动态路由

Spring Cloud Gateway网关的使用和Nacos下发路由配置实现Spring Cloud Gateway的动态路由 一、微服务网关概述 1.1 微服务网关诞生背景 不同的微服务一般会有不同的网络地址&#xff0c;而外部客户端可能需要调用多个服务的接口才能完成一个业务需求&#xff0c;如果让客户端…

泰凌微被暂缓审议:利润下滑遭关注,实控人王维航存在大额负债

1月12日&#xff0c;上海证券交易所披露的信息显示&#xff0c;泰凌微电子&#xff08;上海&#xff09;股份有限公司&#xff08;下称“泰凌微”&#xff09;的首发申请被科创板上市委暂缓审议。据贝多财经了解&#xff0c;上市委现场问题对该公司提出多个问题。 根据申请文件…

用详细实例说明和典型案例实现对分治法进行全面分析 | C++

第一篇 分治法 目录 第一篇 分治法 ●前言 ●一、分治法是什么&#xff1f; 1.简要介绍 2.生活实例 ●二、分治法的典型案例——硬币问题 1.具体问题 2.代码展示&#xff08;C&#xff09; 3.程序代码结果展示 ●总结 前言 简单的来说&#xff0c;算法就是用计算机程序代…

菲中工商贸投资合作签约活动在京举办

2023年1月3日至5日&#xff0c;中菲两国元首亲切会谈后&#xff0c;共同发布了成果丰硕的二十八条内容的联合声明。1月3日至9日&#xff0c;由菲律宾菲中人民友好促进会与中国联合国采购促进会在京联合举办了“菲中工商贸投资合作签约仪式”及“中菲合作项目对接洽谈周”活动。…

FPGA:逻辑函数的代数法化简

文章目录逻辑函数的最简形式逻辑函数的代数化简法并项法吸收法消去法配项法示例1示例2逻辑函数的最简形式 1&#xff0e;化简逻辑函数的意义 LABAˉBAˉBˉ(AAˉ)BAˉBˉ1⋅BAˉBˉBAˉ\begin{aligned} L & A B\bar{A} B\bar{A} \bar{B} \\ & (A\bar{A}) B\bar{A} \ba…

PELT——Per Entity Load Tracking

0. 前言&#xff1a; 今天写第一篇Linux内核调度子系统的文章&#xff0c;首先整理PELT负载追踪方法&#xff0c;之前的基础知识在后续的文章中share出来。文章的写成基本上是在几位内核大佬的文章基础之上完成的&#xff0c;有些地方的文字是直接引用的&#xff0c;但本文只用…

SpringBoot上传文件到Minio服务器

前言 本文主要介绍如何使用SpringBoot上传到minio服务器。 没什么可多说的&#xff0c;公司用什么咱们开发研究什么就完事了。直接分享核心代码。 核心代码 minio依赖 <!-- minio依赖 --><dependency><groupId>io.minio</groupId><artifactI…

ArcGIS基础实验操作100例--实验91栅格欧式分配

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 空间分析篇--实验91 栅格欧式分配 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

第01讲:什么是kubernetes

一、什么是k8s&#xff1f; kubernetes&#xff0c;简称 K8s&#xff0c;是用 8 代替 8 个字符“ubernete”而成的缩写。是一个开源 的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes 的目标是让部署容器化的 应用简单并且高效(powerful),Kubern…