广度优先搜索(BFS,Breadth-First Search)
一、BFS 的 "生活化故事":像找迷宫出口一样 "层层扩散"
你可以把 BFS 想象成 "小时候玩迷宫的最稳妥办法":假设你站在迷宫的起点,想找到出口,但你分不清哪条路是对的。这时你不会一头扎进某条路死磕(那是深度优先搜索 DFS),而是先把离你一步之遥的所有路口 都标记出来(这些是 "第一层");然后从这些第一层路口出发,再把离它们一步之遥、且没被标记过的路口 标记出来("第二层");以此类推,直到某一层的人喊 "我找到出口了!"------ 这就是 BFS 的核心逻辑:层层递进、地毯式搜索。

从学术角度说,BFS 的雏形最早出现在 19 世纪数学家康托尔、若尔当对图结构的研究中,后来被系统应用到计算机领域。它没有 "一个人灵光一闪发明" 的戏剧性故事,但它的思路完全贴合普通人的直觉 ------ 比如你想找朋友圈里和你隔 3 个人的好友,就是先找直接好友(1 层)、再找他们的好友(2 层)、最后找这层人的好友(3 层),直到找到目标。
二、BFS 的核心作用:解决 "步数最少" 的问题
BFS 的核心价值就在于 "层层扩散" 的特性,主要能解决两类问题:
- 最核心 :在无权图(所有边没有权重 / 权重都相同) 中,找到从起点到目标的最短路径(步数最少) ------ 比如迷宫里 "最少走几步到出口"、朋友圈 "你和 XX 隔了几个人";
- 其他常用作用 :
- 遍历图 / 树的所有节点(比如爬虫从一个网页出发,爬完整个网站的所有页面);
- 判断两个节点是否可达(比如判断迷宫里起点能不能到出口、两个用户是否在同一个社交网络里);
- 找离起点最近的所有目标(比如找你家周边 1 公里内的所有超市)。
三、BFS 的具体可执行代码(朋友圈找人示例)
下面用 "找朋友圈里和你隔几步的好友" 为例,写一个通俗易懂的 BFS 代码,注释详细,可直接复制运行。
python
from collections import deque
def bfs_friend_circle(friend_graph, start_person, target_person):
"""
BFS实现:找朋友圈中从start_person到target_person的最短步数(隔几个人)
:param friend_graph: 朋友圈关系图,格式为 {人: [好友列表], ...}
:param start_person: 起点(你)
:param target_person: 目标(要找的人)
:return: 最短步数(比如直接好友返回1,无交集返回-1),最短路径(比如["你", "张三", "李四"])
"""
# 1. 异常处理:起点/目标不在朋友圈里
if start_person not in friend_graph or target_person not in friend_graph:
return -1, []
# 2. 特殊情况:找自己
if start_person == target_person:
return 0, [start_person]
# 3. 初始化:队列(存储待探索的人+当前步数+路径),已访问集合(避免重复找)
# deque是Python的双端队列,popleft()效率高,适合BFS的"先进先出"
queue = deque()
queue.append((start_person, 0, [start_person])) # (当前人, 步数, 路径)
visited = set()
visited.add(start_person)
# 4. 核心循环:层层探索好友
while queue:
# 取出队列最前面的人(保证"层层处理")
current_person, current_step, current_path = queue.popleft()
# 遍历当前人的所有好友
for friend in friend_graph[current_person]:
# 如果好友是目标,直接返回结果
if friend == target_person:
new_path = current_path + [friend]
return current_step + 1, new_path
# 如果好友没被访问过,加入队列继续探索
if friend not in visited:
visited.add(friend)
new_path = current_path + [friend]
queue.append((friend, current_step + 1, new_path))
# 5. 遍历完都没找到,说明无交集
return -1, []
# ------------------- 测试代码 -------------------
if __name__ == "__main__":
# 构建朋友圈关系图
# 说明:"你"的好友是"张三""李四";"张三"的好友是"你""王五";"王五"的好友是"张三""赵六";以此类推
friend_graph = {
"你": ["张三", "李四"],
"张三": ["你", "王五"],
"李四": ["你", "赵六"],
"王五": ["张三", "赵六"],
"赵六": ["李四", "王五", "孙七"],
"孙七": ["赵六"],
"周八": ["吴九"],
"吴九": ["周八"]
}
# 测试1:找"你"到"孙七"的最短步数和路径
step, path = bfs_friend_circle(friend_graph, "你", "孙七")
if step == -1:
print("你和孙七不在同一个朋友圈里")
else:
print(f"你到孙七最短需要隔{step}个人,路径:{' -> '.join(path)}")
# 测试2:找"你"到"周八"的最短步数和路径
step2, path2 = bfs_friend_circle(friend_graph, "你", "周八")
if step2 == -1:
print("\n你和周八不在同一个朋友圈里")
else:
print(f"你到周八最短需要隔{step2}个人,路径:{' -> '.join(path2)}")
代码运行结果
你到孙七最短需要隔3个人,路径:你 -> 张三 -> 王五 -> 赵六 -> 孙七
你和周八不在同一个朋友圈里
代码关键部分解释
- 队列(deque):BFS 的核心工具,用 "先进先出" 保证 "层层处理"------ 先处理完 "你" 的直接好友,再处理好友的好友,不会漏掉任何一层;
- 已访问集合(visited):避免重复探索(比如 "你" 找 "张三","张三" 又找 "你",会无限循环);
- 路径记录:每个队列元素都带 "当前路径",找到目标时直接返回完整路径,不用额外回溯;
- 步数计算:每往下一层,步数 + 1,保证返回的是 "最少步数"。
四、总结
- 故事核心:BFS 像 "迷宫里层层找出口",核心是 "先进先出、地毯式扩散",没有戏剧性发明故事,但贴合日常思维;
- 核心作用:解决无权图的 "最短步数" 问题,还能遍历图、判断节点是否可达(比如朋友圈是否有交集);
- 代码关键:用队列(deque)实现 "层层处理",用已访问集合避免循环,是入门图算法的首选。