这是来自维基百科的拓扑排序的伪代码:
L ← Empty list that will contain the sorted nodes
while there are unmarked nodes do
select an unmarked node n
visit(n)
function visit(node n)
if n has a temporary mark then stop (not a DAG)
if n is not marked (i.e. has not been visited yet) then
mark n temporarily
for each node m with an edge from n to m do
visit(m)
mark n permanently
unmark n temporarily
add n to head of L
我想非递归地写它而不会丢失cicle检测。
问题是我不知道该怎么做,我已经想到了很多方法。基本上问题是要做DFS,但要记住"当前路径" (它适用于"临时标记"以上伪代码中的某些节点)。因此,使用堆栈的传统方法没有任何帮助,因为当使用堆栈(并且将每个节点的邻居放入其中)时,我将节点放在那里,即使我将在未确定的未来中看到它们""我只想跟踪我当前路径上的节点" (我看到它穿过一个迷宫,带着一条线我离开了我 - 当我看到一个死胡同时,我转过身来"包裹着踩踏"当这样做时,在任何一点上时间我想要记住节点"线程位于它们上面"以及线程已经至少一次的节点)。任何提示我指向正确方向的提示?我的意思是 - 我应该考虑使用2个堆栈而不是1个,也许是其他一些数据结构?
或许这个算法没问题,我应该以递归的形式保留它(我只担心超过"递归深度"对于足够大的图形)?
答案 0 :(得分:3)
显然,你使用了一个堆栈,但是你无论如何都不会放置所有相邻的节点:无论如何都会产生一个具有错误大小复杂度的DFS(它假设非节点的数量是二次的)平行边缘,否则可能更糟糕)。相反,您将当前节点与指示要访问的下一个节点的状态一起存储。你总是在堆栈顶部工作,即这样的事情:
std::stack<std::pair<node, iterator> stack;
stack.push(std::make_pair(root, root.begin()));
while (!stack.empty()) {
std::pair<node, iterator>& top = stack.top();
if (top.second == top.first.begin()) {
mark(top.first);
// do whatever needs to be done upon first visit
}
while (top.second != top.first.end() && is_marked(*top.second)) {
++top.second;
}
if (top.second != top.first.end()) {
node next = *top.second;
++top.second;
stack.push(std::make_pair(next, next.first());
}
else {
stack.pop();
}
}
此代码假定节点具有begin()
和end()
,产生适当的迭代器以迭代相邻节点。沿着那些线的某些东西,可能通过边缘的间接肯定存在。它还假设有可用于访问节点标记的功能。在一个更现实的imlementation,可能会使用一些BGL属性映射。是否可以使用std::stack<T>
来重新呈现堆栈取决于是否需要访问当前堆栈上的节点:std::stack
不提供此类访问。但是,基于任何STL序列容器创建合适的堆栈实现是微不足道的。