深度优先搜索:迷宫探险家的终极指南

深度优先搜索(DFS)是一种用于遍历或搜索树、图等数据结构的算法,其核心思想是“一条路走到底,遇到死路就回头”。DFS通过递归或栈的方式实现,优先探索一条路径直到无法继续,然后回溯到上一个岔路口选择另一条路径。这种方法适合寻找所有路径或判断连通性,但不一定找到最短路径。DFS在迷宫寻路、图的连通性判断、岛屿数量问题等场景中有广泛应用。与广度优先搜索(BFS)相比,DFS更注重深度探索,而BFS则更注重广度扩散。DFS的实现需要注意防止死循环和递归深度问题,通常通过标记已访问节点来避免重复探索。


一、迷宫探险家的故事

想象你是一个勇敢的探险家,站在迷宫的入口。你的目标是找到出口,或者探索所有的通道。

你有一条“探险原则”:

  • 每次遇到岔路口,都优先选择一条路一直走到底,直到走不通为止。
  • 如果遇到死路,就原路返回到上一个岔路口,换一条路继续走。
  • 直到所有路都走过,或者找到出口。

这就是**深度优先搜索(DFS)**的核心思想!


二、DFS的过程(图解版)

假设迷宫如下(S是起点,E是出口):

S - A - B - E
    |
    C

探险家怎么走?

  1. 从S出发,走到A。
  2. 从A有两条路:先选A-B。
  3. 从B走到E,找到出口,任务完成!
  4. 如果B-E是死路,返回A,换A-C继续探索。

每次都“走到底”,走不通就回头。


三、DFS的实现方式

1. 递归(像套娃一样)

每到一个新路口,就“递归”地往下走,走不通就返回。

伪代码:

def dfs(当前位置):
    标记当前位置为已访问
    如果当前位置是出口:
        结束
    对于当前位置的每一个相邻点:
        如果没走过:
            dfs(相邻点)

2. 用栈(stack)

你可以用一个“栈”来记录走过的路。每次走新路就把位置压栈,遇到死路就弹栈回退。


四、DFS的特点

  • 优先走到底,不怕走弯路。
  • 适合找“是否有路可达”,或者“所有路径”。
  • 可能不是最短路(有更短的路但你没走到)。
  • 适合用递归或栈实现。

五、生活化比喻

  • DFS像什么?
    像你在迷宫里“钻牛角尖”,每次都一条路走到黑,走不通再回头。
  • BFS(广度优先)像什么?
    像你带着一群小伙伴,大家一起一层一层地探索,先把离入口最近的地方都走一遍。

六、DFS口诀

一条路,走到底,
遇到死路就回去。
所有岔路都要试,
深度优先不放弃!


七、DFS的应用

  • 迷宫寻路
  • 图的连通性判断
  • 拓扑排序
  • 岛屿数量问题
  • 递归遍历树结构

我们继续用“迷宫探险家”的故事,深入讲解深度优先搜索(DFS),并带你了解它的更多细节、代码实现、常见变种和与BFS的对比。


一、迷宫探险家DFS的详细过程

1. 递归版探险

你每到一个新房间(格子),就:

  • 先在门口做个记号(标记已访问)。
  • 看看是不是出口,是就欢呼!
  • 不是出口,就挑一条没走过的路继续走。
  • 走到死路就原路返回,换另一条路。

递归的本质:每次“深入”一个新房间,等探索完再“退回”上一个房间。

2. 栈版探险

你带着一个背包(栈),每走一步就把当前位置放进背包。遇到死路就从背包里拿出上一个位置,回头。


二、DFS代码实现(以二维迷宫为例)

假设迷宫是一个二维数组,0表示路,1表示墙。

1. 递归实现

def dfs(maze, x, y, visited):
    # 越界或遇到墙或已访问
    if x < 0 or x >= len(maze) or y < 0 or y >= len(maze[0]) or maze[x][y] == 1 or visited[x][y]:
        return False
    # 到达出口
    if (x, y) == (出口坐标):
        print("找到出口!")
        return True
    visited[x][y] = True
    # 四个方向探索
    for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
        if dfs(maze, x+dx, y+dy, visited):
            return True
    return False

2. 栈实现(非递归)

def dfs_stack(maze, start, end):
    stack = [start]
    visited = set()
    while stack:
        x, y = stack.pop()
        if (x, y) == end:
            print("找到出口!")
            return True
        if (x, y) in visited:
            continue
        visited.add((x, y))
        for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
            nx, ny = x+dx, y+dy
            if 0 <= nx < len(maze) and 0 <= ny < len(maze[0]) and maze[nx][ny] == 0:
                stack.append((nx, ny))
    return False

三、DFS的常见变种

  1. 全路径搜索
    想找出所有从入口到出口的路径?DFS可以在递归时记录路径,走到出口时保存下来。

  2. 计数问题
    比如“岛屿数量”问题,每遇到一个新岛屿就用DFS把它全部标记。

  3. 图的连通分量
    用DFS遍历所有节点,统计有多少个“独立区域”。


四、DFS与BFS的对比

特点DFS(深度优先)BFS(广度优先)
探索方式一条路走到底一圈一圈地扩散
适合场景找所有路径、连通性找最短路径
实现方式递归/栈队列
可能结果不是最短路径一定是最短路径
空间消耗递归深度/栈深度队列长度

口诀:
DFS钻牛角尖,BFS广撒网。


五、DFS的注意事项

  • 防止死循环:一定要标记已访问,否则会在环形迷宫里转圈圈。
  • 递归深度:递归太深可能会栈溢出,Python默认递归深度有限。
  • 路径记录:如果要输出路径,可以在递归参数里加上当前路径列表。

六、DFS应用举例

  • 迷宫寻路
  • 图的连通性
  • 岛屿数量
  • 拓扑排序
  • 组合/排列问题(如八皇后、数独)

七、DFS动画演示推荐


八、DFS探险家口诀升级版

一条路,走到底,
遇到死路就回去。
所有岔路都要试,
深度优先不放弃!
递归栈,记得清,
防止死循环,标记行!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值