【问题标题】:Transforming recursive DFS-based topological sort into a non-recursive algorithm (without losing cycle detection)将基于 DFS 的递归拓扑排序转换为非递归算法(不丢失循环检测)
【发布时间】:2015-02-25 17:13:12
【问题描述】:

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

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

或者也许这个算法没问题,我应该把它保留为递归形式。对于足够大的图,我只担心超过“递归深度”。

【问题讨论】:

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


    【解决方案1】:

    显然,您会使用堆栈,但无论如何您都不会放置所有相邻节点:无论如何,这将产生具有错误大小复杂度的 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() 产生合适的迭代器来迭代相邻节点。沿着这些思路,可能通过边缘间接的东西肯定会存在。它还假设有可用于访问节点标记的函数。在更现实的实现中,可能会使用 BGL 属性映射。是否可以使用std::stack&lt;T&gt; 表示堆栈取决于当前堆栈上的节点是否需要访问:std::stack 不提供这种访问权限。但是,基于任何 STL 序列容器创建合适的堆栈实现是微不足道的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-28
      • 2016-06-17
      • 2020-09-05
      • 1970-01-01
      相关资源
      最近更新 更多