在 BFS 代码中,queue
的 Expansion(扩展) 和 Generation(生成) 是两个关键概念,决定了节点何时被标记为 visited
,以及搜索顺序如何进行。这两种策略在不同场景下有不同的适用性,理解它们的区别对于正确使用 BFS 以及避免冗余计算至关重要。
🔍 1. Expansion(扩展)
概念
- 节点在出队(poll)时才标记为
visited
。 - 这意味着该节点在
queue
中可能会出现多次,但只有在真正访问它时才会检查是否已经访问过。 - 适用于无环图或某些特定场景,如无权图的最短路径问题。
特点
✅ 保证访问顺序 :所有的相邻节点都会先入队,但只有在出队时才被确认访问。
✅ BFS 仍然按层级遍历 ,但 queue
可能包含重复节点。
❌ 可能导致冗余入队 ,但 visited.contains(curNode)
可以防止重复处理。
代码示例
ini
java
复制编辑
public List<GraphNode> BFS(GraphNode start){
List<GraphNode> result = new ArrayList<>();
Set<GraphNode> visited = new HashSet<>();
Deque<GraphNode> queue = new ArrayDeque<>();
queue.offer(start);
while(!queue.isEmpty()){
GraphNode curNode = queue.poll();
if(visited.contains(curNode)){
continue; // 避免重复处理
}
result.add(curNode);
visited.add(curNode); // 在出队时标记 visited
for(GraphNode nei: curNode.neighbors){
queue.offer(nei);
}
}
return result;
}
适用场景
- 搜索整个图的连通性问题(如遍历所有可达节点)。
- 无权图的最短路径搜索(虽然 Dijkstra 更高效)。
- 适用于不连通的图 ,因为可以在
queue.poll()
时检查visited
。
🔄 2. Generation(生成)
概念
- 节点在入队(offer)时就被标记为
visited
。 - 这样可以防止一个节点被多次入队,减少
queue
中的冗余数据。 - 适用于所有 BFS 相关问题,尤其适用于有环图和连通性检查。
特点
✅ 防止冗余入队 ,提高效率。
✅ BFS 仍然按层级遍历 ,但每个节点只会在 queue
中出现一次。
❌ 在某些场景下可能不适用,例如多次访问同一节点是必须的情况。
代码示例
scss
java
复制编辑
public List<GraphNode> BFS(GraphNode start){
List<GraphNode> result = new ArrayList<>();
Set<GraphNode> visited = new HashSet<>();
Deque<GraphNode> queue = new ArrayDeque<>();
queue.offer(start);
visited.add(start); // 在入队时标记 visited
while(!queue.isEmpty()){
GraphNode curNode = queue.poll();
result.add(curNode);
for(GraphNode nei: curNode.neighbors){
if(!visited.contains(nei)){
visited.add(nei); // 在入队时标记 visited
queue.offer(nei);
}
}
}
return result;
}
适用场景
- 连通性检查:所有相连的节点只会被访问一次,不会重复入队。
- 防止环的重复访问:避免无限循环。
- 更高效的 BFS,适用于大多数图遍历问题。
📊 3. Expansion vs. Generation 总结对比
策略 | 何时标记 visited |
是否可能重复入队 | 优缺点 | 适用场景 |
---|---|---|---|---|
Expansion(扩展) | 出队 (poll() ) 时 |
✅ 可能 | ✅ 适用于遍历所有路径,但可能冗余入队 ❌ 需要额外检查 visited.contains(curNode) |
无权图最短路径问题、连通性检查 |
Generation(生成) | 入队 (offer() ) 时 |
❌ 不会 | ✅ 更高效,防止重复入队 ❌ 不能处理需要多次访问的情况 | 有环图遍历、连通性检查、一般 BFS 问题 |
🔥 4. 如何选择?
✅ 如果问题涉及连通性检查、一般 BFS 遍历,推荐使用 Generation
(入队时标记 visited
)。
✅ 如果问题允许重复入队(例如计算路径数),可以使用 Expansion
(出队时检查 visited
)。
✅ 在 最短路径问题
中,通常建议使用 Generation
以减少冗余计算。
🚀 5. 结论
-
queue
在 BFS 中有 两种不同的使用方式:- Expansion (出队时标记
visited
,可能重复入队) - Generation (入队时标记
visited
,避免重复入队)
- Expansion (出队时标记
-
大多数情况下,
Generation
更优 ,但Expansion
在某些特殊情况下更合适。 -
了解 Expansion vs. Generation 的区别,可以帮助我们更高效地使用 BFS 解决问题!🚀