一、深度优先搜索(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
特性 | DFS | BFS |
---|---|---|
核心数据结构 | 递归栈/显式栈 | 队列 |
路径性质 | 不一定最短(需遍历所有路径) | 首次到达即最短路径(无权图) |
适用场景 | 排列组合、约束满足问题(如数独) | 迷宫最短路径、层序遍历、社交网络 |
时间复杂度 | O(分支数^深度)(依赖剪枝) | O(节点数) |
空间复杂度 | O(深度)(递归栈) | O(节点数) |
🔥 终极算法武器库,助你突破编程思维瓶颈!
从排列组合到复杂约束问题,从无权图到带权图,掌握DFS与BFS核心逻辑,轻松驾驭算法面试与工程实践!🚀