将基于递归DFS的拓扑排序转换为非递归算法(不会丢失循环检测)

时间:2014-12-29 13:51:25

标签: c++ algorithm recursion graph topological-sort

这是来自维基百科的拓扑排序的伪代码:

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个,也许是其他一些数据结构?

或许这个算法没问题,我应该以递归的形式保留它(我只担心超过"递归深度"对于足够大的图形)?

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序列容器创建合适的堆栈实现是微不足道的。