DFS算法实战:经典例题代码解析

DFS

例题

路径之谜

代码如下:

python 复制代码
n = int(input())
north_target = list(map(int, input().split()))
west_target = list(map(int, input().split()))
#当前剩余箭数
north_rem = north_target[:]
west_rem = west_target[:]

dic = [(0, -1), (0, 1), (-1, 0), (1, 0)]

path = []

vis = [[False] * n for i in range(n)]
#可行性检查
def valid(x, y):
    return 0 <= x < n and 0 <= y < n and not vis[x][y]

def dfs(x, y):
    #递归出口:到达终点
    if x == n - 1 and y == n - 1:
        #检查所有靶是否用完
        if all(x == 0 for x in north_rem) and all(y == 0 for y in west_rem):
            return True
        return False

    for i in range(4):
        xx, yy = x + dic[i][0], y + dic[i][1]
        if valid(xx, yy):
            if west_rem[xx] <= 0 or north_rem[yy] <= 0:
                continue
            #选择这个格子
            west_rem[xx] -= 1
            north_rem[yy] -= 1
            vis[xx][yy] = True
            path.append(xx * n + yy)
            
            if dfs(xx, yy):
                return True
            
            #回溯
            vis[xx][yy] = False
            path.pop()
            west_rem[xx] += 1
            north_rem[yy] += 1
    return False

vis[0][0] = True
north_rem[0] -= 1
west_rem[0] -= 1
path.append(0)
dfs(0, 0)
print(' '.join(map(str, path)))

全球变暖

代码如下:

python 复制代码
import sys
sys.setrecursionlimit(10**6)
dic = [(0, 1), (0, -1), (1, 0), (-1, 0)]
def dfs(x, y):
    vis[x][y] = 1
    if mapp[x][y - 1] == '#' and mapp[x][y + 1] == '#' and mapp[x - 1][y] == '#' and mapp[x + 1][y] == '#':
        global flag
        flag = 1

    for i in range(4):
        xx, yy = x + dic[i][0], y + dic[i][1]
        if mapp[xx][yy] == '#' and vis[xx][yy] == 0:
            dfs(xx, yy)

n = int(input())
mapp = []
ans = 0
vis = [[0] * n for _ in range(n)]
for i in range(n):
    mapp.append(list(input()))

for i in range(n):
    for j in range(n):
        if mapp[i][j] == '#' and vis[i][j] == 0:
            flag = 0
            dfs(i, j)
            if flag == 0:
                ans += 1
print(ans)

飞机降落

代码如下:

python 复制代码
import sys
sys.setrecursionlimit(10 ** 6)

def can_schedule(planes, visited, current_time, count, n):
    # 递归出口:所有飞机都已降落
    if count == n:
        return True

    for i in range(n):
        if not visited[i]:
            T, D, L = planes[i]
            # 最早可以开始降落的时间
            start = max(current_time, T)
            # 最晚可以开始降落的时间
            latest = T + D
            # 如果可以安排
            if start <= latest:
                visited[i] = True
                if can_schedule(planes, visited, start + L, count + 1, n):
                    return True
                visited[i] = False  # 回溯
    return False

def solve():
    T = int(input().strip())
    for _ in range(T):
        n = int(input().strip())
        planes = []
        for _ in range(n):
            t, d, l = map(int, input().split())
            planes.append((t, d, l))

        visited = [False] * n
        if can_schedule(planes, visited, 0, 0, n):
            print("YES")
        else:
            print("NO")

if __name__ == "__main__":
    solve()

数字接龙

代码如下:

python 复制代码
import sys
sys.setrecursionlimit(10**6)

dic = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]

# 存储已经连接过的线段
lines = []

def has_cross(x, y, xx, yy):
    """检查对角线移动是否会与已有路径交叉"""
    # 右下
    if xx == x + 1 and yy == y + 1:
        line = {(x+1, y), (x, y+1)}
        return line in lines
    # 左下
    elif xx == x + 1 and yy == y - 1:
        line = {(x+1, y), (x, y-1)}
        return line in lines
    # 右上
    elif xx == x - 1 and yy == y + 1:
        line = {(x-1, y), (x, y+1)}
        return line in lines
    # 左上
    elif xx == x - 1 and yy == y - 1:
        line = {(x-1, y), (x, y-1)}
        return line in lines
    return False

def dfs(x, y, step):
    if step == n * n:
        if x == n - 1 and y == n - 1:
            return True
        return False

    for i in range(8):
        xx, yy = x + dic[i][0], y + dic[i][1]
        
        # 边界检查
        if not (0 <= xx < n and 0 <= yy < n):
            continue

        # 访问检查
        if vis[xx][yy]:
            continue

        # 数字序列检查
        if grid[xx][yy] != (grid[x][y] + 1) % k:
            continue

        # 对角线移动的交叉检查
        if i in [1, 3, 5, 7]:  # 1:右上, 3:右下, 5:左下, 7:左上
            if has_cross(x, y, xx, yy):
                continue
        
        # 记录当前要走的线段(用于后续交叉判断)
        line = {(x, y), (xx, yy)}
        lines.append(line)
        vis[xx][yy] = True
        path.append(i)

        if dfs(xx, yy, step + 1):
            return True

        # 回溯
        path.pop()
        vis[xx][yy] = False
        lines.pop()
    
    return False


n, k = map(int, input().split())
grid = []
path = []

for i in range(n):
    grid.append(list(map(int, input().split())))

# 起点检查
if grid[0][0] != 0:
    print("-1")
else:
    vis = [[False] * n for _ in range(n)]
    vis[0][0] = True
    
    if dfs(0, 0, 1):
        print("".join(map(str, path)))
    else:
        print("-1")

玩具蛇

代码如下:

python 复制代码
dic = [(0, 1), (1, 0), (0, -1), (-1, 0)]
vis = [[False] * 4 for _ in range(4)]
ans = 0

def valid(x, y):
    return 0 <= x < 4 and 0 <= y < 4 and not vis[x][y]
def dfs(x, y, count):
    if count == 16:
        global ans
        ans += 1
        return

    for i in range(4):
        xx, yy = x + dic[i][0], y + dic[i][1]
        if valid(xx, yy):
            vis[xx][yy] = True
            dfs(xx, yy, count + 1)
            vis[xx][yy] = False

for i in range(4):
    for j in range(4):
        vis[i][j] = True
        dfs(i, j, 1)
        vis[i][j] = False
print(ans)

|-------|--------------------------|---------------------|
| 特征 | 找一条路径 | 找所有路径/计数 |
| 函数返回值 | bool | void/None |
| 递归出口 | return True/False | return或return False |
| 递归调用 | if dfs(...): return True | dfs(...) |
| 找到解后 | 立即返回,停止搜索 | 计数,继续搜索 |
| 典型题目 | 路径之谜,飞机降落 | 玩具蛇,八皇后计数 |

迷宫

代码如下:

python 复制代码
# 迷宫地图
maze = [
    "UDDLUULRUL",
    "UURLLLRRRU",
    "RRUURLDLRD",
    "RUDDDDUUUU",
    "URUDLLRRUU",
    "DURLRLDLRL",
    "ULLURLLRDU",
    "RDLULLRDDD",
    "UUDDUDUDLL",
    "ULRDLUURRR"
]

# 方向映射
dirs = {
    'U': (-1, 0),
    'D': (1, 0),
    'L': (0, -1),
    'R': (0, 1)
}

n = 10
# 状态数组:0未访问,1能走出,-1走不出
state = [[0] * n for _ in range(n)]


def dfs(x, y):
    """返回是否能走出迷宫"""
    # 如果已经走出迷宫
    if x < 0 or x >= n or y < 0 or y >= n:
        return True

    # 如果已经计算过
    if state[x][y] != 0:
        return state[x][y] == 1

    # 临时标记为正在访问(防止循环)
    state[x][y] = -1

    # 获取当前格子的方向
    direction = maze[x][y]
    dx, dy = dirs[direction]
    nx, ny = x + dx, y + dy

    # 递归判断下一个格子
    result = dfs(nx, ny)

    # 记录结果
    state[x][y] = 1 if result else -1
    return result


# 统计能走出的人数
ans = 0
for i in range(n):
    for j in range(n):
        if dfs(i, j):
            ans += 1

print(ans)

vis[x][y] = -1 的作用是:

  1. 防止循环递归:在环路中,第二次访问时能立即返回

  2. 作为临时状态:表示"正在计算中",避免重复计算

  3. 记忆化:记录已经计算过的点,提高效率

N皇后问题

代码如下:

python 复制代码
def dfs(x):
    if x == n + 1:
        global ans
        ans += 1
        return
    #尝试x行的每一列
    for y in range(1, n + 1):
        #如果列或对角线上已有皇后,跳过
        if vis1[y] or vis2[x + y] or vis3[x - y + n]:
            continue
        #放置皇后,标记占用
        vis1[y] = vis2[x + y] = vis3[x - y + n] = True
        #递归放置下一行
        dfs(x + 1)
        #回溯
        vis1[y] = vis2[x + y] = vis3[x - y + n] = False

n = int(input())
vis1 = [False] * (n + 1)#列占用情况,下标从1到n, (索引0闲置不用)
vis2 = [False] * (2 * n + 1)#(/方向)占用情况,覆盖从2到2n
vis3 = [False] * (2 * n + 1)#(\方向)
ans = 0
dfs(1)
print(ans)

模板

类型一:遍历/填充型 DFS(不需要回溯)

适用场景:连通分量、洪水填充、岛屿问题、拓扑排序

python 复制代码
def dfs(x, y):
    # 1. 标记当前节点已访问
    visited[x][y] = True
    
    # 2. 处理当前节点(可选)
    # 例如:统计、判断、记录等
    process_current(x, y)
    
    # 3. 遍历所有相邻节点
    for dx, dy in directions:
        nx, ny = x + dx, y + dy
        
        # 4. 合法性检查
        if not (0 <= nx < n and 0 <= ny < n):
            continue
        if visited[nx][ny]:
            continue
        if not is_valid(nx, ny):  # 其他条件,如是否为陆地
            continue
        
        # 5. 递归搜索(不需要回溯)
        dfs(nx, ny)
    
    # 注意:这里没有回溯操作!

类型二:路径搜索型 DFS(需要回溯)

适用场景:迷宫路径、哈密顿路径、数独、八皇后、骑士巡游

python 复制代码
def dfs(x, y):
    # 1. 递归出口:到达目标状态
    if (x, y) == (target_x, target_y):
        if all_conditions_met():
            return True
        return False
    
    # 2. 遍历所有可能的选择
    for dx, dy in directions:
        nx, ny = x + dx, y + dy
        
        # 3. 合法性检查
        if not (0 <= nx < n and 0 <= ny < n):
            continue
        if visited[nx][ny]:
            continue
        if not check_constraints(nx, ny):  # 其他约束
            continue
        
        # 4. 做出选择(记录状态)
        visited[nx][ny] = True
        path.append(get_id(nx, ny))
        update_resources(nx, ny)  # 如有资源约束
        
        # 5. 递归搜索
        if dfs(nx, ny):
            return True
        
        # 6. 撤销选择(回溯)
        visited[nx][ny] = False
        path.pop()
        restore_resources(nx, ny)  # 恢复资源
    
    return False

# 7. 主函数中初始化起点
visited[start_x][start_y] = True
path.append(start_id)
init_resources(start_x, start_y)
dfs(start_x, start_y)

BFS

例题

迷宫

python 复制代码
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

代码如下:

python 复制代码
from collections import deque

# 迷宫数据(30行50列)
maze = [
    "01010101001011001001010110010110100100001000101010",
    "00001000100000101010010000100000001001100110100101",
    "01111011010010001000001101001011100011000000010000",
    "01000000001010100011010000101000001010101011001011",
    "00011111000000101000010010100010100000101100000000",
    "11001000110101000010101100011010011010101011110111",
    "00011011010101001001001010000001000101001110000000",
    "10100000101000100110101010111110011000010000111010",
    "00111000001010100001100010000001000101001100001001",
    "11000110100001110010001001010101010101010001101000",
    "00010000100100000101001010101110100010101010000101",
    "11100100101001001000010000010101010100100100010100",
    "00000010000000101011001111010001100000101010100011",
    "10101010011100001000011000010110011110110100001000",
    "10101010100001101010100101000010100000111011101001",
    "10000000101100010000101100101101001011100000000100",
    "10101001000000010100100001000100000100011110101001",
    "00101001010101101001010100011010101101110000110101",
    "11001010000100001100000010100101000001000111000010",
    "00001000110000110101101000000100101001001000011101",
    "10100101000101000000001110110010110101101010100001",
    "00101000010000110101010000100010001001000100010101",
    "10100001000110010001000010101001010101011111010010",
    "00000100101000000110010100101001000001000000000010",
    "11010000001001110111001001000011101001011011101000",
    "00000110100010001000100000001000011101000000110011",
    "10101000101000100010001111100010101001010000001000",
    "10000010100101001010110000000100101010001011101000",
    "00111100001000010000000110111000000001000000001011",
    "10000001100111010111010001000110111010101101111000"
]

n = 30  # 行数
m = 50  # 列数

# 方向顺序:D < L < R < U(字典序最小)
# 对应坐标变化:下、左、右、上
directions = [
    (1, 0, 'D'),   # 下
    (0, -1, 'L'),  # 左
    (0, 1, 'R'),   # 右
    (-1, 0, 'U')   # 上
]

def bfs():
    # 队列:存储 (x, y)
    queue = deque()
    queue.append((0, 0))
    
    # 记录路径:pre[x][y] = (上一个x, 上一个y, 方向字符)
    pre = [[None] * m for _ in range(n)]
    pre[0][0] = (-1, -1, '')  # 起点的前驱
    
    # 记录是否访问过
    visited = [[False] * m for _ in range(n)]
    visited[0][0] = True
    
    while queue:
        x, y = queue.popleft()
        
        # 到达终点
        if x == n-1 and y == m-1:
            # 回溯路径
            path = []
            while pre[x][y][2] != '':
                path.append(pre[x][y][2])
                x, y = pre[x][y][0], pre[x][y][1]
            # 逆序输出
            return ''.join(path[::-1])
        
        # 按字典序顺序探索四个方向
        for dx, dy, direction in directions:
            nx, ny = x + dx, y + dy
            
            # 边界检查
            if 0 <= nx < n and 0 <= ny < m:
                # 检查是否可通行且未访问
                if maze[nx][ny] == '0' and not visited[nx][ny]:
                    visited[nx][ny] = True
                    pre[nx][ny] = (x, y, direction)
                    queue.append((nx, ny))
    
    return ""

result = bfs()
print(result)

走迷宫

代码如下:

python 复制代码
from collections import deque

n, m = map(int, input().split())
maze = []
for i in range(n):
    maze.append(list(map(int, input().split())))

x1, y1, x2, y2 = map(int, input().split())
x1, y1, x2, y2 = x1 - 1, y1 - 1, x2 - 1, y2 - 1

direction = [(0, -1), (0, 1), (-1, 0), (1, 0)]

def bfs():
    queue = deque()
    queue.append((x1, y1))

    vis = [[False] * m for _ in range(n)]
    vis[x1][y1] = True

    distance = [[0] * m for _ in range(n)]

    while queue:
        x, y = queue.popleft()
        if x == x2 and y == y2:
            return distance[x][y]

        for i in range(4):
            xx, yy = x + direction[i][0], y + direction[i][1]
            if 0 <= xx < n and 0 <= yy < m:
                if maze[xx][yy] == 1 and not vis[xx][yy]:
                    vis[xx][yy] = True
                    distance[xx][yy] = distance[x][y] + 1
                    queue.append((xx, yy))
    return -1

print(bfs())

模板

python 复制代码
from collections import deque

def bfs():
    # 1. 创建队列
    queue = deque()
    
    # 2. 起点入队
    queue.append((start_x, start_y))
    
    # 3. 标记起点已访问
    visited[start_x][start_y] = True
    
    # 4. 记录前驱(用于回溯路径)
    pre = [[None] * m for _ in range(n)]
    pre[start_x][start_y] = (-1, -1, '')
    
    # 5. 方向数组(按字典序)
    directions = [(1,0,'D'), (0,-1,'L'), (0,1,'R'), (-1,0,'U')]
    
    # 6. 主循环:队列不为空就一直搜索
    while queue:
        # 取出队首元素
        x, y = queue.popleft()
        
        # 7. 到达终点
        if x == end_x and y == end_y:
            # 回溯路径
            path = []
            while pre[x][y][2] != '':
                path.append(pre[x][y][2])
                x, y = pre[x][y][0], pre[x][y][1]
            return ''.join(path[::-1])
        
        # 8. 探索四个方向
        for dx, dy, direction in directions:
            nx, ny = x + dx, y + dy
            
            # 边界检查
            if 0 <= nx < n and 0 <= ny < m:
                # 可通行且未访问
                if maze[nx][ny] == '0' and not visited[nx][ny]:
                    visited[nx][ny] = True
                    pre[nx][ny] = (x, y, direction)
                    queue.append((nx, ny))
    
    return ""

为什么用队列,不用栈

python 复制代码
队列(BFS):先进先出 → 一层一层搜索 → 找最短路径
栈(DFS):后进先出 → 一条路走到黑 → 找所有路径

visited 什么时候标记?

python 复制代码
# ? 正确:入队时标记
if not visited[nx][ny]:
    visited[nx][ny] = True  # 立即标记
    queue.append((nx, ny))

# ? 错误:出队时标记(会重复入队)
while queue:
    x, y = queue.popleft()
    visited[x][y] = True  # 太晚了!

如何记录步数?

python 复制代码
# 方法1:用distance数组
distance = [[0] * m for _ in range(n)]
distance[nx][ny] = distance[x][y] + 1

# 方法2:用层数变量
queue.append((x, y, step+1))

如何输出最短路径长度?

python 复制代码
# 不需要记录具体路径时
if x == end_x and y == end_y:
    return distance[x][y]  # 返回步数

BFS模板的简化版(只求步数)

python 复制代码
from collections import deque

def bfs():
    queue = deque()
    queue.append((0, 0))
    visited = [[False] * m for _ in range(n)]
    visited[0][0] = True
    distance = [[0] * m for _ in range(n)]
    
    while queue:
        x, y = queue.popleft()
        
        if x == n-1 and y == m-1:
            return distance[x][y]
        
        for dx, dy in [(1,0), (0,-1), (0,1), (-1,0)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < n and 0 <= ny < m:
                if maze[nx][ny] == '0' and not visited[nx][ny]:
                    visited[nx][ny] = True
                    distance[nx][ny] = distance[x][y] + 1
                    queue.append((nx, ny))
    return -1
相关推荐
数据知道2 小时前
claw-code 源码分析:从 TypeScript 心智到 Python/Rust——跨栈移植时类型、边界与错误模型怎么对齐?
python·ai·rust·typescript·claude code·claw code
智者知已应修善业2 小时前
【51单片机单按键切换广告屏】2023-5-17
c++·经验分享·笔记·算法·51单片机
广州灵眸科技有限公司2 小时前
为RK3588注入澎湃算力:RK1820 AI加速卡完整适配与评测指南
linux·网络·人工智能·物联网·算法
qinian_ztc2 小时前
frida 14.2.18 安装报错解决
算法·leetcode·职场和发展
hhh3u3u3u2 小时前
Visual C++ 6.0中文版安装包下载教程及win11安装教程
java·c语言·开发语言·c++·python·c#·vc-1
AI应用实战 | RE2 小时前
012、检索器(Retrievers)核心:从向量库中智能查找信息
人工智能·算法·机器学习·langchain
凤年徐2 小时前
C++手撕红黑树:从0到200行,拿下STL map底层核心
c++·后端·算法
Thomas.Sir2 小时前
AI 医疗之罕见病/疑难病辅助诊断系统从算法到实现【表型驱动与知识图谱推理】
人工智能·算法·ai·知识图谱
tankeven2 小时前
动态规划专题(03):区间动态规划从原理到实践(未完待续)
c++·算法·动态规划