基于LCA的树型BFS机器人绕路路径规划优化:解决路径顺序错误与节点重复问题
基于LCA的树型BFS机器人绕路路径规划优化:解决路径顺序错误与节点重复问题
看起来你已经搭好了基于LCA的树型BFS绕路框架,但在路径顺序和重复节点上踩了坑——我之前做类似的机器人路径规划时也遇到过几乎一模一样的问题,咱们一步步拆解来修复。
先排查现有代码的核心问题
- LCA查找逻辑有漏洞:当前代码只对比两条路径的前
minLength个节点,当其中一个节点本身是另一个的祖先时(比如currentNode是B,neighborPtr是D),循环会提前break,导致找不到正确的LCA。 - 路径构建与队列插入顺序完全搞反:你要的是
current→LCA→neighbor的执行顺序,但原代码用push_front循环插入,会把路径彻底反转,机器人会走完全相反的路线。 - 无重复节点检查:直接往队列塞节点,会导致同一个节点多次出现在BFS队列里,干扰机器人的路径执行逻辑。
修正后的完整实现方案
我把代码重新梳理了一遍,每一步都加了针对性的注释,完全贴合你的需求:
else { std::shared_ptr<Node> ancestorCurrent = currentNode; std::shared_ptr<Node> ancestorNeighbor = neighborPtr; std::vector<std::shared_ptr<Node>> pathCurrentToRoot; std::vector<std::shared_ptr<Node>> pathNeighborToRoot; // 1. 构建当前节点到根的路径(先收集current到root,再反转成root在前的顺序) while (ancestorCurrent) { pathCurrentToRoot.push_back(ancestorCurrent); ancestorCurrent = ancestorCurrent->parent; } std::reverse(pathCurrentToRoot.begin(), pathCurrentToRoot.end()); // 2. 构建邻居节点到根的路径(同样处理成root在前的顺序) while (ancestorNeighbor) { pathNeighborToRoot.push_back(ancestorNeighbor); ancestorNeighbor = ancestorNeighbor->parent; } std::reverse(pathNeighborToRoot.begin(), pathNeighborToRoot.end()); // 3. 修复LCA查找逻辑:找到两条路径最后一个匹配的节点 std::shared_ptr<Node> lca = nullptr; size_t minLen = std::min(pathCurrentToRoot.size(), pathNeighborToRoot.size()); for (size_t i = 0; i < minLen; ++i) { if (pathCurrentToRoot[i] == pathNeighborToRoot[i]) { lca = pathCurrentToRoot[i]; } else { break; } } // 额外处理边界场景:当一条路径是另一条的前缀(比如current本身就是LCA) if (!lca) { if (pathCurrentToRoot.size() < pathNeighborToRoot.size() && pathNeighborToRoot[pathCurrentToRoot.size()-1] == pathCurrentToRoot.back()) { lca = pathCurrentToRoot.back(); } else if (pathNeighborToRoot.size() < pathCurrentToRoot.size() && pathCurrentToRoot[pathNeighborToRoot.size()-1] == pathNeighborToRoot.back()) { lca = pathNeighborToRoot.back(); } } // 4. 只有当LCA不是current或neighbor本身时,才需要规划绕路(否则直接可达) if (lca) { std::vector<std::shared_ptr<Node>> detourPath; // 构建current到LCA的上行路径(包含current,直到LCA) std::shared_ptr<Node> temp = currentNode; while (temp != lca) { detourPath.push_back(temp); temp = temp->parent; } detourPath.push_back(lca); // 把LCA加入路径 // 构建LCA到neighbor的下行路径(先收集neighbor到LCA的反向路径,再反转成正序) std::vector<std::shared_ptr<Node>> neighborReversePath; temp = neighborPtr; while (temp != lca) { neighborReversePath.push_back(temp); temp = temp->parent; } std::reverse(neighborReversePath.begin(), neighborReversePath.end()); // 把下行路径追加到绕路路径末尾 detourPath.insert(detourPath.end(), neighborReversePath.begin(), neighborReversePath.end()); // 5. 去重:避免队列中已有的节点重复插入 std::unordered_set<std::shared_ptr<Node>> queueNodes; for (const auto& node : bfsDeque) { queueNodes.insert(node); } // 跳过当前机器人所在的节点(已经在currentNode,无需重复加入队列) auto startIt = detourPath.begin(); if (!detourPath.empty() && detourPath.front() == currentNode) { startIt++; } // 收集需要插入的节点(已去重) std::vector<std::shared_ptr<Node>> nodesToAdd; for (auto it = startIt; it != detourPath.end(); ++it) { if (queueNodes.find(*it) == queueNodes.end()) { nodesToAdd.push_back(*it); queueNodes.insert(*it); } } // 6. 按正确顺序插入队列头部:用insert保证路径顺序不变 bfsDeque.insert(bfsDeque.begin(), nodesToAdd.begin(), nodesToAdd.end()); // 7. 更新机器人状态到绕路的第一个节点 if (!bfsDeque.empty()) { std::shared_ptr<Node> firstWaypoint = bfsDeque.front(); *x = firstWaypoint->x; *y = firstWaypoint->y; currentNode = firstWaypoint; robotCurrentX = *x; robotCurrentY = *y; goalReached = false; std::cout << "Detour planned. First waypoint: " << *x << "," << *y << std::endl; return; } } }
几个关键优化点的说明
- LCA查找的边界修复:新增了对“单节点是另一节点祖先”场景的处理,再也不会出现找不到LCA的情况。
- 严格对齐需求的路径顺序:先构建
current→LCA的上行路径,再构建LCA→neighbor的下行路径,完全复现你例子中D→B→A→C→E的正确顺序。 - 队列插入逻辑修正:用
insert替代循环push_front,彻底解决路径顺序反转的问题;同时加入去重检查,杜绝重复节点。 - 冗余节点跳过:机器人已经在
currentNode上,无需将其重复加入队列,直接从下一个绕路节点开始。
用你的例子验证
针对你给出的测试场景:currentNode=D,neighborPtr=E:
- 生成的绕路路径为
D→B→A→C→E - 跳过已在的D节点,将
B→A→C→E按顺序插入队列头部 - 机器人下一步会移动到B,完全符合预期的绕路逻辑
内容来源于stack exchange
相关产品推荐
相关产品推荐

