Queue 在 BFS 中的 Expansion 和 Generation 的区别

在 BFS 代码中,queueExpansion(扩展)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,避免重复入队)
  • 大多数情况下,Generation 更优 ,但 Expansion 在某些特殊情况下更合适。

  • 了解 Expansion vs. Generation 的区别,可以帮助我们更高效地使用 BFS 解决问题!🚀


相关推荐
灰灰勇闯IT9 小时前
KMP算法在鸿蒙系统中的应用:从字符串匹配到高效系统级开发(附实战代码)
算法·华为·harmonyos
小龙报9 小时前
【算法通关指南:数据结构和算法篇 】队列相关算法题:3.海港
数据结构·c++·算法·贪心算法·创业创新·学习方法·visual studio
csuzhucong9 小时前
一阶魔方、一阶金字塔魔方、一阶五魔方
算法
五花就是菜9 小时前
P12906 [NERC 2020] Guide 题解
算法·深度优先·图论
辞旧 lekkk9 小时前
【c++】封装红黑树实现mymap和myset
c++·学习·算法·萌新
星轨初途9 小时前
C++的输入输出(上)(算法竞赛类)
开发语言·c++·经验分享·笔记·算法
n***F87510 小时前
SpringMVC 请求参数接收
前端·javascript·算法
Liangwei Lin10 小时前
洛谷 P1025 [NOIP 2001 提高组] 数的划分
算法
yuuki23323310 小时前
【C++】类和对象(上)
c++·后端·算法
dangdang___go10 小时前
动态内存管理||malloc和free.realloc和calloc
c语言·开发语言·算法·动态内存管理