当前位置: 首页 > 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;​…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…...