【常用算法:查找篇】11.DFS与BFS核心原理及实战全解析

在这里插入图片描述

一、深度优先搜索(DFS):递归回溯的暴力美学

1. 核心思想与搜索状态树

  • 核心逻辑:通过递归深入探索状态树,遇终点或死胡同则回溯,穷举所有可能路径。
  • 状态树模型:以全排列为例,树的层级对应元素选择顺序,叶子节点为完整解。
            根节点(无元素)
            /   |   \
           1    2    3 (第1层选第1个元素)
         / \  / \  / \
        2 3 1 3 1 2 (第2层选第2个元素)
       /   \/   \/   \
      3     2     2     1 (叶子节点:完整排列)
    

2. 经典问题与代码实现

全排列问题(无重复元素)
def permute(nums):
    n, result = len(nums), []
    used = [False] * n
    def dfs(path):
        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)
                path.pop()
                used[i] = False
    dfs([])
    return result
# 输出:[[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
组合问题(C(n, m))
def combine(n, m):
    result, path = [], []
    def dfs(start, depth):
        if depth == m:
            result.append(path.copy())
            return
        for i in range(start, n+1):
            path.append(i)
            dfs(i+1, depth+1)  # 剪枝:后续元素递增
            path.pop()
    dfs(1, 0)
    return result
# 示例:combine(4, 2) → [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
八皇后问题(位运算优化)
def solve_n_queens(n):
    col = [False] * n
    left_diag = [False] * (2*n-1)  # y-x + n-1
    right_diag = [False] * (2*n-1)  # y+x
    result = []
    def dfs(y, path):
        if y == n:
            result.append(path)
            return
        for x in range(n):
            if not col[x] and not left_diag[y-x+n-1] and not right_diag[y+x]:
                col[x] = left_diag[y-x+n-1] = right_diag[y+x] = True
                dfs(y+1, path + [x])
                col[x] = left_diag[y-x+n-1] = right_diag[y+x] = False
    dfs(0, [])
    return result
# 输出:8皇后问题的92种解(以列坐标表示)

3. 剪枝策略

  • 约束剪枝:如组合问题中强制递增选择,避免重复组合。
  • 位运算压缩状态:八皇后问题用布尔数组标记列和对角线,O(1)时间判断合法性。
  • 记忆化搜索:记录已访问状态,避免重复计算(如DFS判图环)。

二、广度优先搜索(BFS):分层扩展的最短路径神器

1. 核心思想与数据结构

  • 核心逻辑:用队列实现逐层扩展,首次到达终点即最短路径(适用于无权图)。
  • 关键组件
    • 队列(Queue):存储待探索节点,先进先出。
    • 访问标记(visited):避免重复访问,O(1)查询。
    • 父节点表(parent):路径还原,从终点回溯到起点。

2. 迷宫最短路径问题

Python代码实现
from collections import deque
def bfs_maze(maze, start, end):
    rows, cols = len(maze), len(maze[0])
    visited = [[False]*cols for _ in range(rows)]
    parent = {}
    queue = deque([start])
    visited[start[0]][start[1]] = True
    directions = [(-1,0),(1,0),(0,-1),(0,1)]
    
    while queue:
        x, y = queue.popleft()
        if (x, y) == end:
            # 路径还原
            path = []
            while (x, y) in parent:
                path.append((x, y))
                x, y = parent[(x, y)]
            return [start] + path[::-1]
        for dx, dy in directions:
            nx, ny = x+dx, y+dy
            if 0<=nx<rows and 0<=ny<cols and not visited[nx][ny] and maze[nx][ny]==0:
                visited[nx][ny] = True
                parent[(nx, ny)] = (x, y)
                queue.append((nx, ny))
    return None  # 无解
优化:双向BFS
  • 原理:从起点和终点同时搜索,相遇时终止,减少搜索层数。
  • 适用场景:大规模迷宫(如105×105网格)。

3. 扩展:Dijkstra算法(带权图最短路径)

  • 核心差异:用优先队列(堆)替代普通队列,每次选择当前最短距离节点扩展。
  • 松弛操作:更新相邻节点距离,确保全局最优。
import heapq
def dijkstra(graph, start):
    dist = {node: float('inf') for node in graph}
    dist[start] = 0
    heap = [(0, start)]
    
    while heap:
        current_dist, u = heapq.heappop(heap)
        if current_dist > dist[u]:
            continue
        for v, w in graph[u]:
            if dist[v] > dist[u] + w:
                dist[v] = dist[u] + w
                heapq.heappush(heap, (dist[v], v))
    return dist

三、DFS进阶:数独求解的剪枝艺术

1. 问题建模与约束

  • 规则:行、列、宫(3×3)内数字1-9不重复。
  • 状态表示:二维数组board[9][9],0表示空白格。

2. 优化DFS实现

基础递归+回溯
def solve_sudoku(board):
    blanks = [(i,j) for i in range(9) for j in range(9) if board[i][j]==0]
    
    def is_valid(x, y, num):
        # 行、列、宫检查
        return not (num in board[x] or 
                    any(board[i][y]==num for i in range(9)) or
                    any(board[i][j]==num for i in range((x//3)*3, (x//3+1)*3) for j in range((y//3)*3, (y//3+1)*3)))
    
    def dfs(index):
        if index == len(blanks):
            return True
        x, y = blanks[index]
        for num in range(1, 10):
            if is_valid(x, y, num):
                board[x][y] = num
                if dfs(index+1):
                    return True
                board[x][y] = 0
        return False
    
    dfs(0)
    return board
位运算优化(O(1)合法性检查)
row_mask = [0]*9  # 行已用数字位掩码(每位代表1-9)
col_mask = [0]*9
box_mask = [0]*9  # 宫索引0-8

for i in range(9):
    for j in range(9):
        num = board[i][j]
        if num != 0:
            bit = 1 << (num-1)
            row_mask[i] |= bit
            col_mask[j] |= bit
            box = (i//3)*3 + (j//3)
            box_mask[box] |= bit

def is_valid(x, y, num):
    bit = 1 << (num-1)
    box = (x//3)*3 + (y//3)
    return not (row_mask[x] & bit or col_mask[y] & bit or box_mask[box] & bit)

3. 启发式剪枝(MRV策略)

  • 最少剩余值(MRV):优先填充可填数字最少的空白格,减少分支数。
blanks.sort(key=lambda pos: sum(1 for num in 1..9 if is_valid(*pos, num)))

四、总结:DFS vs BFS

特性DFSBFS
核心数据结构递归栈/显式栈队列
路径性质不一定最短(需遍历所有路径)首次到达即最短路径(无权图)
适用场景排列组合、约束满足问题(如数独)迷宫最短路径、层序遍历、社交网络
时间复杂度O(分支数^深度)(依赖剪枝)O(节点数)
空间复杂度O(深度)(递归栈)O(节点数)

🔥 终极算法武器库,助你突破编程思维瓶颈!
从排列组合到复杂约束问题,从无权图到带权图,掌握DFS与BFS核心逻辑,轻松驾驭算法面试与工程实践!🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无心水

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

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

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

打赏作者

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

抵扣说明:

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

余额充值