我实现了一个简单的DFS(非递归),如果 StartNode 和 EndNode 之间的路径存在,则“测试”。它按预期工作(处理双向/方向图) - 但我无法弄清楚如何存储路径供以后使用。
目前我正在调试打印已访问的节点,但它不是应该存储的内容。
有人可以帮我解决一下 - 我究竟应该存储什么以及在什么时候返回从NodeStart到NodeEnd的节点列表?
以下是示例图表:
这是DFS遍历功能:
bool DFS(CNavigationGraph *pNavGraph, CNavigationGraphNode* pFromNode, CNavigationGraphNode* pToNode)
{
assert(pNavGraph);
assert(pFromNode);
assert(pToNode);
std::vector<CNavigationGraphNode*> vpVisitedNodes;
std::vector<CNavigationGraphNode*> stack;
stack.push_back(pFromNode);
while(!stack.empty())
{
CNavigationGraphNode *pTop = stack.back();
stack.pop_back();
// Ok We've reached pToNode - means, path pFromNode to pToNode available
if(pTop == pToNode)
{
for(int a = 0; a < vpVisitedNodes.size(); a++)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION, "{VISITED} %s",vpVisitedNodes[a]->GetNodeName().data());
}
return true;
}
// Add to visited list
vpVisitedNodes.push_back(pTop);
// Look for adjacent Nodes for pTop
std::vector<CNavigationGraphNode*> vpAdjacentNodes;
pNavGraph->GetAdjacentNodes(pTop->GetNodeName(), vpAdjacentNodes);
for(int x = 0; x < vpAdjacentNodes.size(); x++)
{
// Add to stack if not visited
if(IsVisited(vpVisitedNodes, vpAdjacentNodes[x]) == false)
stack.push_back(vpAdjacentNodes[x]);
}
}
// Path not found
return false;
}
这是调试输出:
查找Node1和Node3之间的路径
<main> [] DFS TRAVERSE TEST (DIRECTIONAL)
<DFS> [] {VISITED} Node1
<DFS> [] {VISITED} Node4
<DFS> [] {VISITED} Node5
<main> [] Path from Node1 to Node3 - YES
查找Node3和Node1之间的路径
<main> [] DFS TRAVERSE TEST (DIRECTIONAL)
<main> [] Path from Node3 to Node1 - NO
答案 0 :(得分:4)
如果我正确理解您的算法(并且它是DFS),则 从起点开始,您将向第一个未访问节点的方向迈出一步。如果从该节点到目标没有路由,则退回并尝试转到下一个未访问的节点,同时小心地管理访问的节点。
你需要添加的只是一个堆栈,你总是将你正在推动的节点推送到, 如果你不得不退后一步,从堆栈弹出它。 此堆栈会将您的路径从start_node存储到目标。它还可以帮助您确定退回的位置。
这是你的代码,最后它的长度比我想象的要长,但现在是:
// I call fromNode: start_node, toNode: target_node.
std::stack<CNavigationGraphNode*> DFS(CNavigationGraph *pNavGraph, CNavigationGraphNode* start_node, CNavigationGraphNode* target_node)
{
using namespace std;
stack<CNavigationGraphNode*> route; // route to target
unordered_set<CNavigationGraphNode*> visited_nodes; // hash set on who is visited
vector<CNavigationGraphNode*> adjacent_nodes;
CNavigationGraphNode* current_node = start_node;
while(current_node!=target_node)
{
pNavGraph->GetAdjacentNodes(current_node->GetNodeName(), adjacent_nodes);
// "for each"; you can use any form of looping through the neighbors of current_node.
bool found_non_visited = false;
for(auto node : adjacent_nodes)
{
if(visited_nodes.find(node) == visited_nodes.end())
{
route.push(current_node);
visited_nodes.insert(node);
current_node = node;
found_non_visited = true;
break;
}
}
if(found_non_visited)
continue;
if(route.empty())
return route; // empty route means no route found
current_node = route.pop();
}
route.push(target);
return route;
}
现在,您可以pop_back
从开始到目标的方式,或pop
从目标开始的方式。如果路由为空,则对应于从原始函数返回false。
Reference on unordered_set和stack。它是用一个桶实现的,所以它比set或map快得多,它通常用红黑树实现。
备注:std::unordered_set
是C ++ 11扩展,如果您使用C ++ 03,请随意用较慢的std::set
替换它。
答案 1 :(得分:3)
好吧,您可以拥有visited
地图(地图:顶点 - >顶点),而不是设置parent
。
在遍历图表时修改地图,这样如果您从节点v
发现了节点u
,请添加parent[v] = u
。初始parent[source] = NULL
。
现在,您所要做的就是迭代(伪代码):
current <- dest
while current != NULL:
print current
current <- parent[current]
它会以相反的顺序为您提供路径。如果要实现路径的原始顺序,可以存储到堆栈(而不是print语句)并迭代堆栈。
这与线程中为BFS解释的想法非常相似:How can I find the actual path found by BFS?