dfs深度优先搜索

图片截图:

一、DFS 核心思想

从根进入,向下走,走到底,向上走;绕树一圈,最后从根退出。 本质:递归 + 系统栈

  • 向下递归时:压栈
  • 递归返回回溯时:弹栈

系统栈保存:返回地址、局部变量、函数参数。

二、四个触发时机(⭐最重要,对应 4 处 printf)

复制代码
void dfs(int u, int fa){
    printf("入 %d\n",u);  // 【1.入】刚访问到u,第一次碰到这个节点(自顶向下,算子)
    for(auto v : e[u]){
        if(v==fa) continue;
        printf("下 %d\n",u);  //【2.下】准备走向子节点v之前,可以多次执行!
        dfs(v, u);
        printf("回 %d\n",u);  //【3.回】从子节点v递归返回之后,可以多次执行!
    }
    printf("离 %d\n",u);  //【4.离】u的所有子节点全部遍历完成,即将离开u(自底向上,算父)
}

✅ 一句话区分

  1. 入(进入节点) :每个节点只会触发 1 次 刚到达 u,第一次访问。做自顶向下的计算(父传给子)
  2. 下(下走,准备进子节点) :每个子分支触发一次,可以多次 在循环里,每准备访问一个孩子 v 就执行一次。u 有几个子节点,就执行几次下。
  3. 回(回来,子节点回溯完毕) :每个子分支触发一次,可以多次 子树dfs(v)全部跑完,回到 u 的时候执行。u 有几个子节点,就执行几次回。
  4. 离(离开节点) :每个节点只会触发 1 次 u 所有子节点全部遍历结束,准备退出 u。做自底向上的计算(子结果传回父)

口诀:1 入,2 下,3 回,4 离

  • 入、离:每个节点仅一次
  • 下、回:有多少个子节点,就执行多少次(多次触碰同一节点)

计算方向

  • 自顶向下:入、下,父亲把信息传给儿子
  • 自底向上:回、离,儿子算完结果传回父亲(子算父)

三、树的 DFS vs 图的 DFS

1)树 DFS(无环,用父节点 fa 判断,不用 vis 数组)

复制代码
void dfs(int u, int fa){
    for(auto v:e[u]){
        if(v == fa) continue; // 不走回父节点
        dfs(v,u);
    }
}
  • fa:记录 u 的父节点,防止往回走。树没有环,不需要 vis 标记。

2)图 DFS(有环!必须 vis 标记)

图存在回边,只靠 fa 不够,要用vis[]记录节点是否已经访问过 ,避免死循环,遍历生成DFS 树

复制代码
int vis[N];
void dfs(int u){
    vis[u]=true; //标记已访问(入的时候标记)
    for(auto v:e[u]){
        if(vis[v]) continue;
        dfs(v);
    }
}

DFS 树:对图做深度优先遍历,遍历过程经过的边构成一棵树,原图剩下的边叫回边(红色打叉的边,不会进 DFS 树)

四、递归与系统栈原理

递归本质靠系统栈自动维护状态:

  • 向下走(递归调用子节点):压栈,保存当前函数现场
  • 走到叶子无法继续:开始回溯
  • 向上返回(递归结束):弹栈,恢复现场,执行回

五、简单例子理解次数(拿第一张图的树)

节点5有 3 个子节点:2,6,7

  • 入:节点 5,1 次
  • 下:准备去 2、准备去 6、准备去 7 → 3 次下
  • 回:从 2 回来、从 6 回来、从 7 回来 → 3 次回
  • 离:全部子树走完,离开节点 5,1 次

所以:下和回是在 for 循环内部,循环几次,就跑几次;入和离在循环外面,每个节点一次。

六、什么时候在哪写代码(做题经验)

  1. 入:记录节点深度、标记访问、初始化节点信息(父→子传值)
  2. 下:进入子树前预处理,保存当前状态
  3. 回:子树计算完成,收集子节点结果(子→父汇总,树形 DP 最常用!)
  4. 离:节点全部子树处理完,计算当前节点最终信息、回溯撤销标记(回溯题)

七、易错点总结

  1. 树 dfs 参数fa,图 dfs 用vis数组,不要混用。
  2. 下、回在 for 循环内,多次执行;入、离在循环外,每个节点仅一次。
  3. 图遍历会产生 DFS 树,原图中没有被选进 DFS 树的边,是回边。
  4. 递归深度太大容易栈溢出(系统栈容量有限),大数据量要手写栈实现迭代版 DFS。

题目1 迷宫方案数

题目2 跳马方案数

题目3 八皇后

题目4 水坑计数

相关推荐
Benny_Tang5 小时前
AT_arc180_d [ARC180D] Division into 3 题解
数据结构·c++·算法
殷色玫瑰5 小时前
C++ STL:stack、queue、priority_queue 与容器适配器详解
java·开发语言·数据结构·c++·算法·visualstudio
Evand J6 小时前
【电机滤波例程1】PMSM永磁同步电机转速与位置估计,MATLAB程序:标准无迹卡尔曼滤波、模型说明与仿真验证
算法·matlab·电机·ekf·负载·卡尔曼滤波
打码的老程是远篁6 小时前
手把手教你玩转大模型——3. Transformer 的位置编码——模型怎么知道谁在前,谁在后?
人工智能·算法
嘎嘎风6 小时前
MiniMind 学习笔记之 06 一张图、一百行:MiniMind 的骨架
算法·源码
用户7341681035486 小时前
只盯被动响应做状态监测?论“控制信号”在变工况故障诊断中的重要作用
算法
Ivanqhz6 小时前
MQA、GQA、稀疏/滑动窗口注意力及混合范式
人工智能·算法·机器学习
罗湖老棍子6 小时前
荒岛野人(信息学奥赛一本通- P1637)(洛谷-P2421)
算法·数论·枚举·裴蜀定理·扩展欧几里得·扩展中国剩余定理
朝朝辞暮i6 小时前
C++ 第 40 章:ROS2 Publisher + Timer + Subscriber + Callback + Executor 完整闭环
开发语言·c++·算法·ros2
400分6 小时前
具身智能系列之从零到「机械臂会放试管」:π0.5 VLA 真机部署完全指南【第一节】
算法