当前位置: 首页 > news >正文

CSP-J 算法基础 深度优先搜索

文章目录

  • 前言
  • 深度优先搜索
    • 通俗解释
    • 例子
    • 深度优先搜索的步骤
    • DFS 的特点
    • 生活中的类比
  • 为什么递归问题会变成深度优先搜索?
    • 递归与深度优先搜索的关系:
    • 递归与系统栈
      • 递归调用的过程:
      • 栈的作用:
    • 递归与系统栈的简单示例
      • 递归实现 DFS 的简单例子:
    • 递归和系统栈的可视化:
  • DFS时栈的变化
    • 步骤 1:开始 DFS,从根节点 `A` 开始
    • 步骤 2:访问左子树 `B`
    • 步骤 3:访问左子树 `D`
    • 步骤 4:`D` 没有子节点,回溯到 `B`
    • 步骤 5:访问右子树 `E`
    • 步骤 6:`E` 没有子节点,回溯到 `B`,再回到 `A`
    • 步骤 7:访问右子树 `C
    • 步骤 8:`C` 没有子节点,回溯到 `A`
      • 总结:
  • 总结


前言

深度优先搜索(Depth First Search, DFS)是图论和树结构中常用的遍历算法之一。在解决许多递归问题、图的连通性问题、路径搜索、迷宫问题等场景时,DFS 都扮演了重要角色。通过“深入”的方式,DFS 先探索一个节点的所有后代,直到无法继续深入为止,然后再回溯到上一个节点。DFS 可以通过递归或使用栈的方式实现,常用于遍历图、树等数据结构。理解和掌握 DFS 是许多复杂算法的基础,特别是在图论中的应用。


深度优先搜索

深度优先搜索(DFS,Depth-First Search)是一种在遍历或搜索树和图结构时常用的算法,简单来说,它的工作方式是:沿着一条路径尽可能深地走下去,直到不能再深入为止,然后回头去找其他还没走过的路径

通俗解释

想象你在一个迷宫里探险,DFS 的方式就像这样:

  1. 从入口出发,你会选择一条通道,并一直走下去,尽量往迷宫的最深处走,直到遇到死路或走到终点。
  2. 如果遇到了死路,你会回头走到最近的分叉点,再选择另一条没走过的路继续深入。
  3. 重复这个过程,直到你探索完所有的路。

这就像你先走得尽量深,而不是先广泛地探索所有的通道,这就是“深度优先”的意思。

例子

如果你要在一棵树里找到某个特定的节点,深度优先搜索会从根节点开始,一直沿着某条分支走到底,如果这条路径没有找到目标节点,就回到上一个节点,换另一条路径继续搜索。

深度优先搜索的步骤

  1. 选一个起点:从根节点(或任意节点)开始。
  2. 深入搜索:沿着一条路径走下去,直到叶子节点或遇到死路。
  3. 回溯:如果某条路走不通,回到上一个分叉点,换另一条路继续。
  4. 继续探索:直到所有节点都被访问过。

DFS 的特点

  • 递归:DFS 通常用递归的方式来实现,因为递归天然适合这种“深入再回头”的过程。
  • 栈结构:如果不用递归,DFS 也可以通过一个栈(stack)来实现,因为栈的后进先出(LIFO)机制可以很好地支持这种“深入再回溯”的过程。
  • 找到的路径可能不是最短的:DFS 走得很深,但不一定是最短路径,因为它不保证先找到的路径就是最优解。

生活中的类比

想象你要检查一个楼里的每一层房间:

  • 深度优先搜索的方法是:你进入楼后,走进第一间房间,再走到房间里的每个子房间(如果有的话),一直探索到底。当你走到尽头后再回到上一个房间,换下一条路继续探索。
  • 这种方法总是优先深入到最里面,而不是先检查所有房间。

这就是深度优先搜索,一个专注于尽量深入探索的搜索策略。

为什么递归问题会变成深度优先搜索?

递归和深度优先搜索(DFS)之间有天然的联系,因为递归本质上是“先深入再回溯”的过程,而这正是深度优先搜索的核心思想。

递归与深度优先搜索的关系:

  • 递归:递归是一种解决问题的方式,通过让函数调用自身来逐步解决子问题。当我们用递归解决一个问题时,先把问题逐步分解成更小的子问题,然后等每个子问题解决后,依次返回结果。
  • 深度优先搜索:DFS 也是一步步深入探索的过程。在树或图的结构中,DFS 会沿着一个分支一直深入到最深处,然后再回溯到上一个节点,继续探索其他分支。

因此,当用递归实现 DFS 时,递归的过程就是深度优先地一层一层深入到子问题(或子节点),直到不能再深入为止。这就是为什么递归天然适合实现深度优先搜索

递归与系统栈

递归函数在执行时,每次调用函数自身时,都会将当前的函数状态(变量、指针等)压入系统栈,这就像给函数按下了一个“暂停键”,保存了当前状态。等递归深入到最底层时,递归开始“回溯”,即逐层从栈中取出保存的状态,恢复之前暂停的函数执行,直到所有递归调用都结束。

递归调用的过程:

  1. 递归进入:每次调用自身时,都会将当前函数状态(比如局部变量、参数等)压入栈中,递归继续深入。这个过程不断往下,直到达到基准条件或不能再递归时停止。
  2. 递归回溯:当递归到达最深处时,会回到上一个调用点,将之前的状态从栈中取出,恢复函数执行,直到完成整个递归。

这与 DFS 的“深入再回溯”的过程完全一致。

栈的作用:

  • (stack)是一种后进先出(LIFO)的数据结构。递归调用时,系统使用栈来保存每一层函数调用的状态,这样可以在函数返回时恢复之前的状态。
  • 系统栈负责管理递归函数的调用关系。在 DFS 中,递归函数每深入到下一层,当前的计算状态就会被压入栈,等到回溯时再从栈中弹出并继续执行。

递归与系统栈的简单示例

递归实现 DFS 的简单例子:

以树的前序遍历为例(先访问根节点,再访问左子树和右子树):

void DFS(TreeNode* root) {if (root == nullptr) {return;}// 访问当前节点cout << root->val << " ";// 递归访问左子树DFS(root->left);// 递归访问右子树DFS(root->right);
}

这个递归函数在每次进入时,会将当前节点的状态压入栈,进入左子树或右子树的递归调用。当到达叶子节点(无子节点)时,函数返回,并从栈中恢复上一个状态,继续未完成的任务。这个过程就是 深度优先搜索 的实现。

递归和系统栈的可视化:

  • 假设我们有一棵二叉树,DFS 从根节点 A 开始:

        A/ \B   C/ \
    D   E
    
  • 递归过程:

    1. 调用 DFS(A),进入 A 节点。
    2. 调用 DFS(B),进入 B 节点。
    3. 调用 DFS(D),进入 D 节点,到达叶子节点,递归开始回溯。
    4. 返回到 B,接着调用 DFS(E)
    5. 返回到 A,然后调用 DFS(C)
  • 系统栈的变化:

    • 每次调用 DFS(X) 时,当前函数状态被压入栈,等函数返回时从栈中弹出并恢复执行。
    • 栈的状态会在递归过程中像“弹簧”一样收缩和扩展,逐层管理函数调用。

DFS时栈的变化

好的,下面我将用字符串图一步一步展示深度优先搜索(DFS)时系统栈的变化。假设我们有一棵二叉树,结构如下:

    A/ \B   C/ \
D   E

步骤 1:开始 DFS,从根节点 A 开始

  • 访问 A
  • 系统栈:[A]
Stack: A

步骤 2:访问左子树 B

  • 进入节点 B
  • 系统栈:[A, B]
Stack: A -> B

步骤 3:访问左子树 D

  • 进入节点 DD 是叶子节点,无子节点)
  • 系统栈:[A, B, D]
Stack: A -> B -> D

步骤 4:D 没有子节点,回溯到 B

  • D 返回到 B
  • 系统栈:[A, B]
Stack: A -> B

步骤 5:访问右子树 E

  • 进入节点 EE 也是叶子节点)
  • 系统栈:[A, B, E]
Stack: A -> B -> E

步骤 6:E 没有子节点,回溯到 B,再回到 A

  • E 返回到 B,再回到 A
  • 系统栈:[A]
Stack: A

步骤 7:访问右子树 `C

  • 进入节点 CC 是叶子节点)
  • 系统栈:[A, C]
Stack: A -> C

步骤 8:C 没有子节点,回溯到 A

  • C 返回到 A
  • 系统栈:[](栈为空,DFS 完成)
Stack: (empty)

总结:

  • 深度优先搜索会在每次访问新节点时将其压入系统栈,直到无法继续深入(遇到叶子节点)。
  • 遇到叶子节点或无子节点时,递归回溯,栈中的节点逐步弹出。
  • 这个过程就是栈的“深入(push)-回溯(pop)”操作的完整体现。

希望这个一步一步的字符串图能够帮助你理解 DFS 时栈的变化!


总结

深度优先搜索是一种通过递归或显式栈来实现的遍历算法,具有简单且直观的特点。在遍历图或树结构时,它优先深入探索某一路径,遇到叶子节点或无法继续时再回溯。DFS 在路径搜索、连通性判定、强连通分量分析等问题中有广泛的应用。掌握 DFS 有助于理解更多高级算法的实现,并为解决复杂问题提供了一种基本的思路。

相关文章:

CSP-J 算法基础 深度优先搜索

文章目录 前言深度优先搜索通俗解释例子深度优先搜索的步骤DFS 的特点生活中的类比 为什么递归问题会变成深度优先搜索&#xff1f;递归与深度优先搜索的关系&#xff1a;递归与系统栈递归调用的过程&#xff1a;栈的作用&#xff1a; 递归与系统栈的简单示例递归实现 DFS 的简…...

LeetCode题练习与总结:基本计算器 Ⅱ--227

一、题目描述 给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-2^31, 2^31 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符串作为数学表达式计算…...

Elasticsearch基础(七):Logstash如何开启死信队列

文章目录 Logstash如何开启死信队列 一、确保 Elasticsearch 输出插件启用 DLQ 支持 二、配置 Logstash DLQ 设置 三、查看死信队列 四、排查 CSV 到 Elasticsearch 数据量不一致的问题 Logstash如何开启死信队列 在 Logstash 中&#xff0c;死信队列&#xff08;Dead Le…...

c语言--力扣简单题目(链表的中间节点)讲解

题目如下&#xff1a; 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点…...

【STM32 Blue Pill编程】-定时器计数模式

定时器计数模式 文章目录 定时器计数模式1、定时器计数模式介绍2、硬件准备及接线3、模块配置3.1 定时器计数模式配置3.2 定时器中断配置3.3 串口配置4、代码实现在本文中,我们将讨论如何在计数器模式下配置 STM32 Blue Pill 定时器模块。 要将定时器用作计数器,我们将其配置…...

【例题】lanqiao1331 二进制中 1 的个数

二进制中 1 的个数 题目描述 给定一个整数 x&#xff0c;输出该数二进制表示中 1 的个数。 例&#xff1a;9 的二进制表示为 1001&#xff0c;有 2 位是 1 &#xff0c;所以函数返回 2。 输入描述 输入 x​ &#xff08;内存空间为 32 位的整数&#xff09;。 输出描述 第一…...

【论文解读】图像序列识别:CRNN技术在场景文本识别中的应用与突破(附论文地址)

论文地址&#xff1a;https://arxiv.org/pdf/1507.05717 这篇文章的标题是《An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition》&#xff0c;作者是Baoguang Shi, Xiang Bai和Cong Yao&#xff0c…...

Vue3+CesiumJS相机定位camera

new Cesium.Camera (scene) 摄像机由位置&#xff0c;方向和视锥台定义。 方向与视图形成正交基准&#xff0c;上和右视图x上单位矢量。 视锥由6个平面定义。每个平面都由 Cartesian4 对象表示&#xff0c;其中x&#xff0c;y和z分量定义垂直于平面的单位矢量&#xff0c;w分量…...

turbo译码算法MAX, MAX_SCALE and MAX_STAR的比较

在Turbo码的译码算法中&#xff0c;MAX、MAX_SCALE和MAX_STAR是涉及对数似然比&#xff08;LLR&#xff09;计算时&#xff0c;对MAP&#xff08;最大后验概率&#xff09;算法或其变种Log-MAP算法中分支度量计算的几种不同处理方式。下面是对这三种方法的比较&#xff1a; 1.…...

关于HarmonyOS的学习

day31 购物车案例 一、加入购物车 1、点击按钮后&#xff0c;把当前这个列表的数据拿到&#xff0c;应该存储到一个数组里面 --- 数据结构&#xff0c;把数据存储进行数组2、假如已经把所有的数据添加数组完毕&#xff0c;最终应该存储进购物车里面&#xff0c;所谓的购物车说…...

【雅特力AT32】搭建模板工程及GPIO点灯操作

目录 AT32模板工程建立及点灯操作 建立AT32模板工程 AT32点灯操作 LED原理图GPIO寄存器LED源码分析 建立AT32模板工程 从0到编译运行详细搭建保姆教程&#xff1a; 【雅特力AT32】Keil 环境&#xff1a;搭建标准库模板工程、使用 AT-Link、Debug 里选择 CMSIS-DAP调试器 下面做…...

实战千问2大模型第三天——Qwen2-VL-7B(多模态)视频检测和批处理代码测试

画面描述:这个视频中,一位穿着蓝色西装的女性站在室内,背景中可以看到一些装饰品和植物。她双手交叉放在身前,面带微笑,似乎在进行一场演讲或主持活动。她的服装整洁,显得非常专业和自信。 一、简介 阿里通义千问开源新一代视觉语言模型Qwen2-VL。其中,Qwen2-VL-72B在大…...

数据库索引底层数据结构之B+树MySQL中的页索引分类【纯理论干货,面试必备】

目录 1、索引简介 1.1 什么是索引 1.2 使用索引的原因 2、索引中数据结构的设计 —— B树 2.1 哈希 2.2 二叉搜索树 2.3 B树 2.4 最终选择之——B树 2.4.1 B树与B树的对比(面向索引)【面试题】 3、MySQL中的页 3.1 页的使用原因 3.2 页的结构 3.2.1 页文件头和页文件…...

编译QT源码时的configure参数须知

文章目录 一、configure help原文二、configure help机译三、features 执行命令得到configure帮助文件 qtsrc/configure --help一、configure help原文 Usage: configure [options] [-- cmake-options]This is a convenience script for configuring Qt with CMake. Options…...

如何利用人工智能大模型来进行数字化营销?

这是一本关于如何利用人工智能大模型来进行数字化营销并驱动业绩增长的书。人工智能大模型是指那些具有超大规模的参数和数据的人工智能模型&#xff0c;它们能够在各种复杂的任务上表现出惊人的能力。 在本书中&#xff0c;你将学习到如何在电商、广告和用户增长等数字化营销业…...

【MRI基础】回波序列长度-echo train length ETL概念

回波序列长度 回波序列长度 (echo train length, ETL) 是磁共振成像 (MRI) 中的一个重要参数&#xff0c;它对图像采集时间和图像质量有显著影响。ETL 是指在单个激励脉冲之后的 MRI 序列中采集的回波数量。通过增加 ETL&#xff0c;可以在一个重复时间 (TR) 内收集多个回波&a…...

(179)时序收敛--->(29)时序收敛二九

1 目录 (a)FPGA简介 (b)Verilog简介 (c)时钟简介 (d)时序收敛二九 (e)结束 1 FPGA简介 (a)FPGA(Field Programmable Gate Array)是在PAL (可编程阵列逻辑)、GAL(通用阵列逻辑)等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域…...

[Visual Stuidio 2022使用技巧]2.配置及常用快捷键

使用vs2022开发WPF桌面程序时常用配置及快捷键。 语言&#xff1a;C# IDE&#xff1a;Microsoft Visual Studio Community 2022 框架&#xff1a;WPF&#xff0c;.net 8.0 一、配置 1.1 内联提示 未开启时&#xff1a; 开启后&#xff1a; 开启方法&#xff1a; 工具-选…...

每日奇难怪题(持续更新)

1.以下程序输出结果是() int main() {int a 1, b 2, c 2, t;while (a < b < c) {t a;a b;b t;c--;}printf("%d %d %d", a, b, c); } 解析:a1 b2 c2 a<b 成立 ,等于一个真值1 1<2 执行循环体 t被赋值为1 a被赋值2 b赋值1 c-- c变成1 a<b 不成立…...

江协科技STM32学习- P13 TIM定时器中断

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…...

git github仓库管理

原文链接&#xff1a;git github仓库管理 拉取镜像 github的仓库有两种下载方式,http和ssh,http是对外公开的,可以直接clone,ssh的一般是自己的或内部的仓库,仓库需要配置ssh-key才能使用git clone. 或者直接网页下载 #https git clone https://github.com/git/git.git #ssh…...

【JavaEE】线程安全性问题,线程不安全是怎么产生的,该如何应对

产生线程不安全的原因 在Java多线程编程中&#xff0c;线程不安全通常是由于多个线程同时访问共享资源而引发的竞争条件。以下是一些导致线程不安全的常见原因&#xff1a; 共享可变状态&#xff1a;当多个线程对共享的可变数据进行读写时&#xff0c;如果没有适当的同步机制&…...

低代码-赋能新能源汽车产业加速前行

在“双碳”战略目标的引领下&#xff0c;全球新能源汽车产业正经历着前所未有的发展和变革&#xff0c;新能源汽车整车制造成为绿色低碳转型的重要领域。在政府的大力扶持下&#xff0c;新能源整车制造领域蓬勃发展&#xff0c;已成为全球汽车产业不可逆转的重要趋势。新能源汽…...

基于UDP的简易网络通信程序

目录 0.前言 1.前置知识 网络通信的大致流程 IP地址 端口号&#xff08;port&#xff09; 客户端如何得知服务器端的IP地址和端口号&#xff1f; 服务器端如何得知客户端的IP地址和端口号&#xff1f; 2.实现代码 代码模块的设计 服务器端代码 成员说明 成员实现 U…...

AI大模型在知识管理平台上的应用:泛微·采知连实现自动采集.精准搜索.智能问答.主动推荐

AI技术的发展&#xff0c;正在推动组织知识管理模式发生变革。知识管理系统通过各种应用实现知识体系落地&#xff0c;当前聚焦于整合生成式AI技术&#xff0c;以提升业务效率。 组织在数字化进程中面临着知识增量增多、知识更新频率变快、知识与业务结合更紧密等挑战&#xff…...

JavaEE:文件内容操作(一)

文章目录 文件内容的读写---数据流字节流和字符流打开和关闭文件文件资源泄漏try with resources 文件内容的读写—数据流 文件内容的操作,读文件和写文件,都是操作系统本身提供了API,在Java中也进行了封装. Java中封装了操作文件的这些类,我们给它们起了个名字,叫做"文…...

无人机视角下落水救援检测数据集

无人机视角下落水救援检测数据集&#xff0c;利用无人机快速搜索落水者对增加受害者的生存机会至关重要&#xff0c;该数据集共收集12万帧视频图像&#xff0c;涵盖无人机高度从10m-60m高度&#xff0c;检测包括落水者&#xff08;11万标注量&#xff09;、流木&#xff08;900…...

openssl+keepalived安装部署

文章目录 OpenSSL安装下载地址编译安装修改系统配置版本 Keepalived安装下载地址安装遇到问题安装完成配置文件 keepalived运行检查运行状态查看系统日志修改服务service重新加载systemd检查配置文件语法错误 OpenSSL安装 下载地址 ​ 考虑到后面设备可能没法连接到外网&…...

float存储原理

float存储原理基于IEEE 754标准&#xff0c;主要包括符号位、指数位和有效数字位三部分。以下是对其存储原理的具体介绍&#xff1a; 符号位&#xff1a;符号位是浮点数中用于表示正负的位。在单精度浮点数&#xff08;32位&#xff09;中&#xff0c;最左边的第1位是符号位&a…...

DAY 9 - 10 : 树

树的概念 定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个节点的有限集合T&#xff0c;它满足两个条件 &#xff1a; 1.有且仅有一个特定的称为根&#xff08;Root&#xff09;的节点。 2.其余的节点可以分为m&#xff08;m≥0&#xff09;个互不相交的…...