DFS、BFS、回溯与 visited 数组在搜索问题中的应用

DFS、BFS、回溯与 visited 数组在搜索问题中的应用

这两天一直在刷算法题,如何在最短时间速成,那就是对同类型题总结。

要理解 DFS、BFS、回溯、visited 数组 的关系,需要先明确每个概念的核心定位,再看它们如何在 “搜索问题” 中协同或区分 —— 本质上,它们都是解决 “从初始状态找到目标状态” 的工具,只是适用场景和实现逻辑不同,而 visited 数组是保障搜索正确性的通用辅助手段。

一、先明确每个概念的核心定义

在分析关系前,先拆解每个概念的本质,避免混淆:

概念

核心定位

典型场景

DFS

深度优先搜索(Depth-First Search),是一种 “一条路走到黑” 的搜索策略。

迷宫路径、连通性判断、拓扑排序

BFS

广度优先搜索(Breadth-First Search),是一种 “逐层扩散” 的搜索策略。

最短路径(无权图)、层序遍历

回溯

是一种 “尝试 - 失败 - 回退” 的解题思想,常基于 DFS 实现,核心是 “撤销选择”。

组合、排列、子集、迷宫回溯

visited 数组

是标记 “已访问状态” 的辅助工具,防止重复访问或无限循环,保障搜索正确性。

所有图 / 矩阵搜索问题

二、四者的核心关系:分层理解

可以按 “策略(DFS/BFS)→ 思想(回溯)→ 工具(visited) ” 的层级理解它们的关系,具体可分为 3 个维度:

1. DFS 与 BFS:并列的搜索策略,互斥但互补

DFS 和 BFS 是解决 “遍历 / 搜索” 问题的两种 基础且互斥的策略—— 同一问题中通常二选一,选择的核心依据是问题需求:

对比维度

DFS(深度优先)

BFS(广度优先)

搜索逻辑

从起点出发,优先深入下一层(“一条路走到黑”),走不通再回退。

从起点出发,先遍历当前层所有节点,再遍历下一层(“逐层扩散”)。

实现方式

递归(天然栈结构)或手动栈

队列(FIFO,保证层级顺序)

核心优势

空间复杂度低(栈深度 ≤ 最大路径长度);适合 “找任意路径”“回溯类问题”。

能找到无权图的最短路径(首次到达目标节点时,路径长度最小)。

典型场景

迷宫找任意路径、组合 / 排列问题、连通分量判断。

迷宫找最短路径、二叉树层序遍历、社交网络 “好友推荐”(找最近联系人)。

:同一迷宫问题

  • 若只需 “找到任意一条通路”:选 DFS(实现简单,递归即可);
  • 若需 “找到最短通路”:必须选 BFS(DFS 可能深入长路径,无法保证最短)。
2. 回溯与 DFS:“思想” 与 “实现载体” 的关系

回溯不是一种独立的搜索策略,而是一种 “尝试 - 回退” 的解题思想,其核心场景是 “需要枚举所有可能,且过程中需撤销无效选择”(如组合、排列、子集问题)。

而 DFS 的 “递归回退” 特性,恰好为回溯思想提供了 天然的实现载体—— 递归的 “调用栈” 会记录每一步的选择,递归返回时自然回到上一步,此时只需 “撤销上一步的选择”,就是完整的回溯流程。

可以理解为:

回溯 = DFS 搜索 + 撤销选择

举个例子(组合问题:从 [1,2,3] 选 2 个元素):
  1. 尝试选 1 → 递归选下一个元素(可选 2 或 3);
  2. 选 1+2:记录组合 [1,2],递归结束后 撤销选择(移除 2)
  3. 再选 1+3:记录组合 [1,3],递归结束后 撤销选择(移除 3)
  4. 最后 撤销选择(移除 1),尝试选 2 → 后续流程同理。

这里的 “撤销选择” 就是回溯的核心,而整个流程是通过 DFS 递归实现的。

注意:回溯几乎只基于 DFS 实现,不会基于 BFS—— 因为 BFS 是 “逐层扩散”,路径分散,无法高效 “回退” 到上一步撤销选择。

3. visited 数组:所有搜索策略的 “通用安全锁”

visited 数组(或标记逻辑)的核心作用是 防止重复访问同一个状态 / 节点,避免无限循环或重复计算,它是 DFS、BFS、回溯的 “通用辅助工具”,但具体用法会根据场景微调:

(1)在 “无状态重复” 的场景:visited 用于标记 “已走节点”

典型场景:矩阵 / 迷宫搜索(迷宫题)。

  • 迷宫中每个格子是 “唯一节点”,一旦走过就不能再走(否则会绕圈,比如从 (0,0)→(0,1) 再回 (0,0),无限循环);
  • 实现方式:访问格子 (x,y) 时,将 map[x][y] 设为 1(等效于 visited 标记),回溯时再改回 0(撤销标记)。
(2)在 “有状态重复” 的场景:visited 用于标记 “已用元素”

典型场景:排列问题(如从 [1,2,3] 生成全排列)。

  • 数组中每个元素是 “唯一元素”,一旦在当前排列中使用过,就不能再用(否则会生成 [1,1,2] 这种无效排列);
  • 实现方式:用 boolean[] visited 数组,visited[i] = true 表示第 i 个元素已用,递归回溯时设为 false(撤销标记)。
(3)不同策略下的 visited 共性:
  • DFS/BFS/ 回溯都需要 visited:只要存在 “重复访问风险”,就必须用;
  • visited 的核心是 “标记状态”:状态可以是 “节点位置”“元素索引”“路径状态” 等,本质是避免无效循环。

三、总结:四者的关系图谱

用一句话串联所有关系:

回溯思想通过 DFS 策略实现,DFS 和 BFS 是并列的搜索策略,三者在解决 “有重复访问风险” 的问题时,都需要借助 visited 数组来保障搜索的正确性。

具体关系图谱如下:

搜索问题

├─ 搜索策略(二选一)

│  ├─ DFS(深度优先):适合回溯、找任意路径

│  │  └─ 回溯思想:基于 DFS 实现,核心是“撤销选择”

│  └─ BFS(广度优先):适合找最短路径(无权图)

└─ 辅助工具

   └─ visited 数组:防止重复访问,DFS/BFS/回溯均需使用

四、实战:用迷宫问题看四者的应用

  1. 策略选择:题目说 “迷宫只有一条通道”,只需找任意路径,所以用 DFS;
  2. 回溯应用:当 DFS 走到死路(如下、右、上、左都走不通),需要 “移除当前位置(path.remove)” 并 “恢复格子状态(map [x][y] = 0)”,这就是回溯;
  3. visited 作用:用 map[x][y] = 1 标记已走格子(等效于 visited),避免绕圈;
  4. BFS 不适用:题目不需要最短路径,用 BFS 会增加队列的空间开销,不如 DFS 简洁。

通过这个例子能清晰看到:DFS 是骨架,回溯是血肉,visited 是安全锁,三者协同解决问题,而 BFS 是另一种骨架(适用于其他场景)。

题目:

迷宫问题_牛客题霸_牛客网

ACM中的AC题_牛客题霸_牛客网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值