算法知识图谱总览 / 图基础与遍历 / DFS深度优先搜索

DFS深度优先搜索

P0basicdeepG-B-1

What · 是什么

递归深入遍历O(V+E),连通性判定与DFS生成树基础;沿一条路径深入到底再回溯,栈/递归实现,空间O(h)

DFS(Depth First Search)是图/树遍历算法,从起点出发,每次尝试向更深的节点走,走到无路可走时回溯。通过递归调用自身或显式栈实现。每个节点仅访问一次(打访问标记)。DFS生成树是原图的一个生成树,其性质可用于求强连通分量、割点、桥等。DFS序列中每个子树对应连续区间,可用于树上问题。

Why · 为什么

直觉 · Intuition

想象走迷宫:每次选一条路走到尽头,走不通了就退回上一个岔路口换路。这就是'深度优先'——优先往深处探索,而非广度优先地逐层展开。

原理 · Principle

DFS的核心是递归调用自身+访问标记。对当前节点打标记后遍历其所有邻居,若邻居未访问则递归。递归调用栈天然实现回溯。时间O(V+E)需用邻接表存储图(均摊O(1)遍历每条边)。

历史 · History

DFS由Tarjan在1972年系统化用于图论算法。递归实现利用函数调用栈,与BFS用队列形成对应。OI Wiki指出大部分竞赛支持无限栈空间。

How · 实现

伪代码

DFS(v):
  mark v as visited
  for u in neighbors(v):
    if u not visited:
      DFS(u)

Python 实现

import sys
sys.setrecursionlimit(1000000)

def dfs_recursive(adj, start, visited=None):
    if visited is None:
        visited = set()
    visited.add(start)
    for v in adj[start]:
        if v not in visited:
            dfs_recursive(adj, v, visited)
    return visited

def dfs_iterative(adj, start):
    visited = set()
    stack = [start]
    while stack:
        u = stack.pop()
        if u in visited:
            continue
        visited.add(u)
        for v in adj[u]:
            if v not in visited:
                stack.append(v)
    return visited

def dfs_tree_edges(adj, start):
    """Return DFS tree edges (parent, child)."""
    visited = set([start])
    edges = []
    def _dfs(u):
        for v in adj[u]:
            if v not in visited:
                visited.add(v)
                edges.append((u, v))
                _dfs(v)
    _dfs(start)
    return edges

if __name__ == "__main__":
    adj = {0: [1, 2], 1: [0, 3], 2: [0, 3], 3: [1, 2]}
    r1 = dfs_recursive(adj, 0)
    r2 = dfs_iterative(adj, 0)
    assert r1 == r2 == {0, 1, 2, 3}, f"DFS failed: {r1}, {r2}"
    tree = dfs_tree_edges(adj, 0)
    assert len(tree) == 3
    # Disconnected graph: only reaches one component
    adj2 = {0: [1], 1: [0], 2: [3], 3: [2]}
    r3 = dfs_recursive(adj2, 0)
    assert r3 == {0, 1}, f"Discon DFS failed: {r3}"
    print("dfs: ALL TESTS PASSED")
PyPy 提示递归DFS在PyPy中可能因递归深度限制(默认1000)而崩溃。用sys.setrecursionlimit调大,或改用迭代栈实现。PyPy递归比CPython慢,深递归建议迭代化。
时间复杂度O(V+E)
空间复杂度O(V)(含递归栈)
注意 · 局限递归深度受栈空间限制,深图(如链)可能栈溢出。非连通图只能访问起点所在连通分量。DFS序列不唯一(取决于邻居遍历顺序)。

易错点

例题

OJ题号难度考点链接
LeetCode2004用 DFS/BFS 统计连通块数量https://leetcode.cn/problems/number-of-islands/
LuoguP39164反向建图 DFS 求可达最大编号https://www.luogu.com.cn/problem/P3916
AtCoderABC277 D3DFS 遍历连通分量求和https://atcoder.jp/contests/abc277
步骤迷你图(节点 = 算法步骤 · 边 = 流程 · 绿色 = 起始步骤)
上游 · 本知识点依赖(前置/变体/组合/共现)

信源

✓ = 已核验 · △ = 未核验