c++ - 在 C++ 中建模任意树(使用迭代器)

标签 c++ boost graph tree tree-traversal

我正在寻找一种方法来为每个节点具有任意数量子节点的树建模。

这个答案建议使用 Boost Graph Library 来完成这个任务:

What's a good and stable C++ tree implementation?

我需要执行的主要操作是树及其子树的遍历函数(前序、子树、叶子)。我还需要从 child 向上收集数据的功能。

BGL 是正确的选择吗?如何实现简单树的先序遍历?在文档中,我只能找到有关常规图表的信息。

编辑:我也知道 tree.hh 库,但它的许可证似乎并不适合所有人。

最佳答案

我已经对这棵树进行了改进。顶点和边迭代器现在都包含在外观中。如果我做出更多重大更改,我会发布它们。我曾在一个小型项目中使用过 tree.hh,但并不十分喜欢它。我会用这个替换它,看看它还需要什么。

#include<iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/graph/graphviz.hpp>

#include <boost/graph/visitors.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_utility.hpp>

// ..............................................
template< typename Tree >
void write_graphviz( std::ostream& os, Tree tree )
{
    os << "digraph G {\n";
    for( auto it : tree )
        os << it << "[label=\"" << tree[ it ] << "\"];\n";
    for( auto it : tree.get_edges( ) )
        os << it.m_source << "->" << it.m_target << ";\n";
    os << "}\n";
}

// ..............................................
template< typename Tree >
const typename boost::graph_traits< Tree >::vertex_descriptor get_vertex( Tree& tree, const typename Tree::out_edge_iterator& iter ) { return iter->m_target; }

template< typename Tree >
const typename boost::graph_traits< Tree >::vertex_descriptor get_vertex( Tree& tree, const typename Tree::vertex_iterator& iter ) { return *iter; }

// ..............................................
template< typename Tree, typename IteratorType >
class tree_iter_type
    : public boost::iterator_facade<
        tree_iter_type< typename Tree, typename IteratorType >
        ,typename Tree::vertex_property_type
        ,boost::forward_traversal_tag
    >
{
public:
    typedef typename tree_iter_type< typename Tree, typename IteratorType > other_type;
    typedef typename boost::graph_traits< Tree >::vertex_descriptor iterator_value_type;
    typedef typename boost::graph_traits< Tree >::edge_descriptor eiterator_value_type;
    typedef typename Tree::vertex_property_type value_type;
private:
    friend class boost::iterator_core_access;
    value_type& dereference( ) const { return tree[ get_vertex( tree, iter ) ]; }
    void increment( ) { ++iter; }
    bool equal( other_type const& other ) const { return iter == other.iter; }
public:
    tree_iter_type( Tree& tree, IteratorType iter ) : tree( tree ), iter( iter ) { }

    //http://stackoverflow.com/questions/31264984/c-compiler-error-c2280-attempting-to-reference-a-deleted-function-in-visual
    other_type& operator=( other_type const& a ){ tree= a.tree, iter= a.iter; return *this; };
    iterator_value_type vertex( ) { return get_vertex( tree, iter ); }
    //how to make this work? It blows things up....
    //iterator_value_type operator ( ) { return get_vertex( tree, iter ); }

private:
    Tree& tree;
    IteratorType iter;
};

// ..............................................
template< typename Tree, typename IterType > //proxy container
class tree_pair_type
{
public:
    typedef typename boost::graph_traits< Tree >::vertex_descriptor vertex_t;
    typedef std::pair< IterType, IterType > iterator_pair_t;
    tree_pair_type( iterator_pair_t pair ) :pair( pair ){ }
    IterType begin( ) { return pair.first; }
    IterType end( ) { return pair.second; }
private:
    iterator_pair_t pair;
};

// ..............................................
template< typename ValueType >
class BGTree
{
public:
    typedef boost::adjacency_list<
        boost::mapS,
        boost::vecS,
        boost::bidirectionalS,
        ValueType >
        tree_t;
    typedef typename ValueType value_type;
    typedef typename boost::graph_traits< tree_t >::vertex_descriptor vertex_t;
    typedef typename boost::graph_traits< tree_t >::edge_descriptor edge_t;

    typedef typename tree_t::vertex_iterator vertex_iterator;
    typedef typename tree_t::edge_iterator edge_iterator;
    typedef typename tree_t::out_edge_iterator out_edge_iterator;

    typedef typename tree_iter_type< tree_t, typename tree_t::out_edge_iterator > out_tree_iterator;
    typedef typename tree_iter_type< tree_t, typename tree_t::vertex_iterator > vertex_tree_iterator;

    typedef tree_pair_type< tree_t, edge_iterator > pair_type;
    typedef tree_pair_type< tree_t, out_tree_iterator > out_pair_type;
    typedef tree_pair_type< tree_t, vertex_tree_iterator > vertex_pair_type;

    //get children
    auto make_out_iterator( vertex_t pos ) { return out_tree_iterator( tree, boost::out_edges( pos, tree ).first ); }
    auto make_out_iterator_end( vertex_t pos ) { return out_tree_iterator( tree, boost::out_edges( pos, tree ).second ); }
    //get sub tree
    auto make_range_iterator( vertex_t pos ) { return vertex_tree_iterator( tree, begin( ) ); }
    auto make_range_iterator_end( vertex_t pos ) { return vertex_tree_iterator( tree, end( ) ); }

public:
    BGTree( const ValueType& root )
        :root( boost::add_vertex( root, tree ) ) {  }

    vertex_t get_root( ) const { return root; }
    vertex_t add_child( const ValueType& item, vertex_t where ) {
        auto temp= boost::add_vertex( item, tree );
        boost::add_edge( where, temp, tree );
        return temp;
    }
    vertex_t get_parent( vertex_t from ) const { return boost::in_edges( from, tree ).first->m_source; }
    auto get_bundle( ) { return boost::get( vertex_bundle, tree ); }
    //vertext set, not by value
    vertex_iterator begin( ) { return boost::vertices( tree ).first; }
    vertex_iterator end( ) { return boost::vertices( tree ).second; }
    ValueType& operator [ ] ( vertex_t at ) { return tree[ at ]; }
    //by index, not by value
    auto get_edges( ) { return pair_type( boost::edges( tree ) ); }

    auto get_children( vertex_t pos= 0 ) {
        return out_pair_type( std::make_pair(
                make_out_iterator( pos ), make_out_iterator_end( pos ) ) );
    }
    auto breadth_first( vertex_t pos= 0 ) {
        return vertex_pair_type( std::make_pair(
            make_range_iterator( pos ), make_range_iterator_end( pos ) ) );
    }
    auto get_vertex_children( vertex_t pos ) { return out_pair_type( boost::out_edges( pos, tree ) ); }
    auto get_boost_graph_tree( ) { return tree; }

private:
    tree_t tree;
    vertex_t root;
};

// .....................................................................................
// .....................................................................................
int main( )
{
    //build tree to match the images in documentation
    //https://en.wikipedia.org/wiki/Tree_traversal
    typedef BGTree< char > char_tree_t;

    char_tree_t tree( 'F' );
    auto last= tree.get_root( );
    last= tree.add_child( 'B' , last );
    tree.add_child( 'A' , last );
    last= tree.add_child( 'D' , last );
    tree.add_child( 'C' , last );
    tree.add_child( 'E' , last );
    last= tree.get_root( );
    last= tree.add_child( 'G' , last );
    last= tree.add_child( 'I' , last );
    last= tree.add_child( 'H' , last );

    // visualization
    std::ofstream os( "./graph.dot" );
    ::write_graphviz( os, tree );

    std::cout << "Pre-order: F, B, A, D, C, E, G, I, H\n";
    for( auto& i : tree.breadth_first( ) )
        std::cout << i << " ";
    std::cout << "\n\n";

    std::cout << "Children of root: B, G\n";
    for( auto& i : tree.get_children( ) )
        std::cout << i << " ";
    std::cout << "\n\n";

    auto it= std::find_if(
        tree.breadth_first( ).begin( ), tree.breadth_first( ).end( ),
        [ ] ( const char& test ) { return test == 'B'; } );
    std::cout << "should be 'B', find_if: " << *it << "\n\n";

    std::cout << "children of 'B' from iterator: A D\n";
    //as it has a referance to tree, could be it.get_children( )?
    for( auto& item : tree.get_children( it.vertex( ) ) )
        std::cout << item << " ";
    std::cout << "\n\n";

    return 0;
}

关于c++ - 在 C++ 中建模任意树(使用迭代器),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40809926/

相关文章:

c++ - 为什么在C++ 11中,我们从vector::resize中的一个函数移至2个版本?

c++ - 我们能得到 lambda 参数的类型吗?

c++ - 如何将我的 Qt 应用程序主窗口始终放在 Windows 任务栏上方?

c++ - 多边形中的 boost 点给出错误结果?

c# - 大型无向图的高效存储、查找和操作

r - iGraph:如何创建具有特定级别的图表?

c++ - 我注意到整数和长整数的大小相同。为什么?

c++ - 在 C++ 中传递命令行参数

c++ - 调用信号的函数堆栈内存和函数控制会发生什么

database - 如何存储公共(public)交通数据