Skip to content

广度优先搜索

广度优先搜索(BFS)是一种图遍历算法,按层遍历节点。

概述

广度优先搜索从起始节点开始,依次访问所有相邻节点,然后再访问下一层节点。

算法步骤

  1. 访问起始节点
  2. 依次访问所有相邻节点
  3. 对每个相邻节点重复步骤 2

实现

python
from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    visited.add(start)
    
    while queue:
        node = queue.popleft()
        print(node, end=' ')
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)
    
    return visited

# 示例图
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

print("BFS 遍历结果:")
bfs(graph, 'A')  # A B C D E F

复杂度分析

复杂度
时间复杂度O(V + E)
空间复杂度O(V)

应用场景

最短路径

python
def bfs_shortest_path(graph, start, end):
    visited = {start}
    queue = deque([(start, [start])])
    
    while queue:
        node, path = queue.popleft()
        
        if node == end:
            return path
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append((neighbor, path + [neighbor]))
    
    return None  # 无路径

# 示例
path = bfs_shortest_path(graph, 'A', 'F')
print(f"\n最短路径: {path}")  # 最短路径: ['A', 'C', 'F']

层序遍历

python
def level_order_traversal(root):
    if root is None:
        return []
    
    result = []
    queue = deque([root])
    
    while queue:
        level_size = len(queue)
        current_level = []
        
        for _ in range(level_size):
            node = queue.popleft()
            current_level.append(node.value)
            
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        result.append(current_level)
    
    return result

# 示例二叉树
class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print("\n层序遍历结果:", level_order_traversal(root))  # [[1], [2, 3], [4, 5]]

无权图最短路径

python
def bfs_distance(graph, start):
    distance = {node: -1 for node in graph}
    distance[start] = 0
    
    queue = deque([start])
    
    while queue:
        node = queue.popleft()
        
        for neighbor in graph[node]:
            if distance[neighbor] == -1:
                distance[neighbor] = distance[node] + 1
                queue.append(neighbor)
    
    return distance

# 示例
distances = bfs_distance(graph, 'A')
print("\n各节点距离:", distances)  # {'A': 0, 'B': 1, 'C': 1, 'D': 2, 'E': 2, 'F': 2}

变体

双向 BFS

python
def bidirectional_bfs(graph, start, end):
    if start == end:
        return [start]
    
    forward_visited = {start}
    backward_visited = {end}
    forward_queue = deque([(start, [start])])
    backward_queue = deque([(end, [end])])
    
    while forward_queue and backward_queue:
        # 前向搜索
        node, path = forward_queue.popleft()
        for neighbor in graph[node]:
            if neighbor in backward_visited:
                # 找到相遇点
                return path + find_path(backward_queue, neighbor)[::-1]
            if neighbor not in forward_visited:
                forward_visited.add(neighbor)
                forward_queue.append((neighbor, path + [neighbor]))
        
        # 后向搜索
        node, path = backward_queue.popleft()
        for neighbor in graph[node]:
            if neighbor in forward_visited:
                # 找到相遇点
                return find_path(forward_queue, neighbor) + path[::-1]
            if neighbor not in backward_visited:
                backward_visited.add(neighbor)
                backward_queue.append((neighbor, path + [neighbor]))
    
    return None

def find_path(queue, target):
    for node, path in queue:
        if node == target:
            return path
    return []

与 DFS 的对比

特性BFSDFS
数据结构队列
空间复杂度O(V)O(V)
最短路径保证不保证
适用场景最短路径、层序遍历连通性检测、拓扑排序

注意事项

  1. 队列顺序: 使用队列保证按层遍历
  2. 访问标记: 必须标记已访问的节点,避免重复访问
  3. 内存使用: 在最坏情况下,可能需要存储所有节点