【问题标题】:Non-recursive post-order graph traversal?非递归后序图遍历?
【发布时间】:2018-11-11 16:55:56
【问题描述】:

我正在寻找一些伪代码、算法或指南,以帮助我为广义图数据结构找到合适的迭代后序遍历算法。

我发现很多资源(如两栈或一栈算法)非常适用于树,但无法处理图表,因为它们无法处理循环/后边、交叉边等。

我已经成功编写了一个递归后序图遍历算法,如下所示:

template<typename V, typename E>
void tGraph<V, E>::RecursivePostOrderSearch(const tGraph& g, const VertexType& u, std::set<VertexType>& visited, std::vector<VertexType>& result)
{
    if (visited.find(u) == visited.end())
    {
        visited.insert(u);

        EdgeSet edgesOut = g.outgoingEdgesOf(u);

        for(typename EdgeSet::const_iterator iter = edgesOut.begin(); iter != edgesOut.end(); iter++)
        {
            RecursivePostOrderSearch(g, iter->second.second, visited, result);
        }

        result.push_back(u);
    }
}

template<typename V, typename E> std::vector<V> tGraph<V, E>::postOrderList(const VertexType& v) const
{
    std::set<V> visited;
    std::vector<V> result;

    RecursivePostOrderSearch(*this, v, visited, result);

    return result;
}

其中V 是节点类型,E 是边缘类型——“权重”对和传入/传出节点对。

如果我在下图中运行::postOrderList(使用根节点A):

我希望按此顺序获得以下节点(边按其权重顺序排列):

  • D, E, F, B, G, C, A

…而且我上面的递归算法确实提供了正确的结果。

但是,我自己尝试将其转换为迭代算法一直是个挑战,而且我在这方面没有任何成功。我已经尝试转换为尾递归,因此我可以转换为迭代,但我无法弄清楚。我也尝试过转换基于树的二栈和一栈算法,但也无法在那里正确复制结果。

我已经看到过类似的堆栈溢出问题,但似乎没有一个涵盖实际的迭代算法、伪代码或这种算法的递归到迭代转换,因此我相信在这个方向上的任何指导都会有帮助。

提前致谢。

【问题讨论】:

    标签: c++ recursion tree iteration graph-theory


    【解决方案1】:

    result.push_back 有问题但是可以通过对每个节点处理两次来处理,使用一个标志来指定是访问孩子还是推回。

    要实现这一点,您可以使用带有“u”和 bool(用于标志)的结构的堆栈/向量。

    类似的东西:

    template<typename V, typename E>
    void tGraph<V, E>::PostOrderSearch(const tGraph& g, const VertexType& u, std::set<VertexType>& visited, std::vector<VertexType>& result)
    {
        std::vector<std::pair<VertexType,bool> > stack;
        stack.push_back(std::pair<VertexType, bool>(u,false));
        for(;;) {
          if (stack.empty()) return; // Done.
          std::pair<VertexType, bool> item=stack.back();
          stack.pop_back();
          VertexType u=item.first;
          if (item.second) {
             // Post-visit
             result.push_back(u);
          }
          else if (visited.find(u)==visited.end()) {
            // Add in reverse order, due to stack
            visited.insert(u);
    
            EdgeSet edgesOut = g.outgoingEdgesOf(u);
    
            stack.push_back(std::pair<VertexType, bool>(u,true));   
    
            for(typename EdgeSet::const_reverse_iterator iter = edgesOut.rbegin(); iter != edgesOut.rend(); iter++)
            {
                stack.push_back(std::pair<VertexType,bool>(iter->second.second,false));
            }
         }
    }
    

    【讨论】:

    • 非常感谢。这行得通;我想我明白我哪里错了。在我尝试解决这个问题时,我试图使用std::set 来标记节点,类似于我使用visited 集的方式。但是,我没有想到用布尔值标记节点会更好,将其推送到堆栈上,并将布尔值作为操作码处理——现在我看到了你的方式,这更有意义了已经实现了。再次,非常感谢。非常感谢您的帮助。
    猜你喜欢
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 2011-11-08
    • 1970-01-01
    相关资源
    最近更新 更多