【数据结构与算法】———深度优先:“死磕 + 回头” 的艺术

深度优先搜索:从 "一条道走到黑" 到搞定算法题

刷抖音时总刷到 "当代年轻人现状:选了一条路就硬走,撞了南墙才回头"?这不就是深度优先搜索(DFS)的精髓嘛!DFS 这算法跟咱年轻人的 "犟脾气" 如出一辙 ------ 遇到岔路口先闷头往前走,走不通了再退回去换条路,主打一个 "不撞南墙不回头,撞了南墙就回溯"。今天咱就用 Python 代码拆解这算法,看看它咋帮咱搞定力扣难题和面试考点。

啥是 DFS?说白了就是 "死磕到底"

DFS 的核心逻辑特简单:从起点出发,先把一条路走到头,实在没路了就退回到上一个路口,换条路继续死磕。就像玩密室逃脱时,你总不能刚看到个门就掉头吧?不得先进去瞅瞅有没有钥匙嘛!

实现 DFS 有两种姿势:

  • 递归版:自带 "后悔药"(函数调用栈帮你记着回头的路)
  • 栈版:手动记路(适合怕递归太深 "爆栈" 的场景)

咱先看个二叉树遍历的例子,这是 DFS 的经典秀场。

二叉树遍历:DFS 的 "开胃小菜"

二叉树的前序遍历(根→左→右)就是典型的 DFS 操作。比如给你棵树:

markdown 复制代码
    1
     \
      2
     /
    3

按 DFS 的思路,先啃根节点 1,再一头扎进右子树,摸到 2 之后又钻进左子树找到 3,完美符合 "一条道走到黑" 的原则。

递归实现:简单到离谱

python 复制代码
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
def dfs_tree(root):
    result = []
    def traverse(node):
        if not node:
            return
        # 先拿根节点的value
        result.append(node.val)
        # 左子树死磕到底
        traverse(node.left)
        # 右子树死磕到底
        traverse(node.right)
    traverse(root)
    return result
# 测试一下
root = TreeNode(1, right=TreeNode(2, left=TreeNode(3)))
print(dfs_tree(root))  # 输出 [1,2,3],跟预期一模一样

这代码简洁得像抖音神曲,几行就搞定。但如果树太深(比如几万层),递归可能会 "栈溢出",这时候就得用栈手动模拟。

栈实现:手动记路不翻车

ini 复制代码
def dfs_stack(root):
    if not root:
        return []
    stack = [root]
    result = []
    while stack:
        node = stack.pop()  # 弹出栈顶元素
        result.append(node.val)
        # 右子树先入栈,因为栈是"后进先出",保证左子树先被访问
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)
    return result
print(dfs_stack(root))  # 同样输出 [1,2,3]

用栈的时候得注意入栈顺序,右子树先塞进去,才能让左子树先被 "啃" 到,这点跟递归版保持一致。

力扣热门题:DFS 实战秀

光说不练假把式,咱拿两道面试高频题练练手,看看 DFS 咋大显神通。

1. 岛屿数量(LeetCode 200):DFS 的 "扫雷" 时刻

这题就像玩扫雷,碰到一个 "1"(陆地),就得把周围所有连在一起的 "1" 都标记成 "0"(水),每扫完一片就是一个岛屿。

python 复制代码
def numIslands(grid):
    if not grid:
        return 0
    rows, cols = len(grid), len(grid[0])
    count = 0
    
    def dfs(i, j):
        # 越界或者不是陆地,直接撤退
        if i < 0 or i >= rows or j < 0 or j >= cols or grid[i][j] != '1':
            return
        # 标记为已访问(改成水)
        grid[i][j] = '0'
        # 上下左右四个方向继续扫
        dfs(i-1, j)  # 上
        dfs(i+1, j)  # 下
        dfs(i, j-1)  # 左
        dfs(i, j+1)  # 右
    
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == '1':
                count += 1
                dfs(i, j)  # 把整个岛屿都"淹"掉
    return count
# 测试用例
grid = [
    ["1","1","0","0","0"],
    ["1","1","0","0","0"],
    ["0","0","1","0","0"],
    ["0","0","0","1","1"]
]
print(numIslands(grid))  # 输出3,完美!

这题的关键是 "标记已访问",不然会重复计算。就像抖音上的 "打卡",去过的地方得打个勾,免得下次又跑过去。

2. 全排列(LeetCode 46):DFS 的 "排列组合" 小课堂

给一串数字,返回所有可能的排列。比如 [1,2,3],得返回 6 种组合。这题用 DFS + 回溯简直绝配,就像穿衣服 ------ 先穿 1,再试 2,最后套 3;不行就脱了 3 换 2,以此类推。

python 复制代码
def permute(nums):
    result = []
    n = len(nums)
    
    def dfs(path, used):
        # 路径长度够了,就加入结果
        if len(path) == n:
            result.append(path.copy())
            return
        for i in range(n):
            if not used[i]:
                # 选当前数字
                used[i] = True
                path.append(nums[i])
                # 继续选下一个
                dfs(path, used)
                # 回溯:把选的数字放回去
                path.pop()
                used[i] = False
    
    dfs([], [False]*n)
    return result
print(permute([1,2,3]))  # 输出所有6种排列,一目了然

这里的 "回溯" 就像抖音上的 "后悔药" 特效,选错了立马撤回重来,特方便。

面试常问:DFS 的 "优缺点" 和 "应用场景"

面试官最爱问 "DFS 适合啥场景?""它跟 BFS 有啥区别?",咱得答得像刷抖音刷到的知识点一样顺口:

  • 适合的场景
    • 迷宫问题(找出口、走迷宫)
    • 排列组合(全排列、子集问题)
    • 连通性问题(岛屿数量、朋友圈)
    • 拓扑排序(课程表问题)
  • 优点:代码简单(递归版几行搞定),内存占用比 BFS 少(不用记一整层的节点)。
  • 缺点:可能绕远路(找不到最短路径),递归太深容易 "爆栈"(比如处理 10 万层的树)。

记住:求最短路径选 BFS,求所有可能路径选 DFS,就像抖音上的 "选对赛道很重要"。

总结:DFS 就是 "死磕 + 回头" 的艺术

DFS 这算法说难也难,说简单也简单。核心就是 "一条道走到黑,走不通就回头",跟咱生活中试错的过程一模一样。多练几道力扣题(比如子集、电话号码的字母组合),你就会发现:哦,原来 DFS 就是这么回事儿,之前真是想复杂了!

最后送句抖音热评:"递归一时爽,一直递归一直爽,直到栈溢出 ------ 那就换栈实现呗!" 掌握 DFS,算法题从此少掉一把头发~

相关推荐
ID_1800790547327 分钟前
淘宝拍立淘按图搜索API接口功能详细说明
大数据·python·json·图搜索算法
java1234_小锋1 小时前
周学会Matplotlib3 Python 数据可视化-绘制折线图(Lines)
开发语言·python·信息可视化·matplotlib·折线图·matplotlib3
用户576905308011 小时前
MCP入门级简单尝试
python·mcp
java1234_小锋1 小时前
一周学会Matplotlib3 Python 数据可视化-绘制直方图(Histogram)
开发语言·python·信息可视化·matplotlib·matplotlib3
数据智能老司机1 小时前
图算法趣味学——最大流算法
数据结构·算法·云计算
Kyln.Wu2 小时前
【python实用小脚本-182】Python一键爬取今日新闻:5分钟生成Word+CSV——再也不用复制粘贴
开发语言·python·word
数据智能老司机2 小时前
图算法趣味学——图着色
数据结构·算法·云计算
数据智能老司机2 小时前
图算法趣味学——启发式引导搜索
数据结构·算法·云计算
秋难降2 小时前
零基础学习SQL(五)——函数详解
python·sql·mysql