为什么C ++ STL不提供任何“树”容器?

时间:2008-10-15 18:52:42

标签: c++ stl tree

为什么C ++ STL不提供任何“树”容器,而最好使用什么呢?

我想将对象层次结构存储为树,而不是使用树作为性能增强...

14 个答案:

答案 0 :(得分:174)

您可能想要使用树的原因有两个:

您希望使用树状结构来镜像问题:
为此,我们有boost graph library

或者您想要一个具有树状访问特征的容器 为此,我们有

基本上这两个容器的特征是它们实际上必须使用树来实现(尽管这实际上不是必需的)。

另见这个问题: C tree Implementation

答案 1 :(得分:89)

可能出于同样的原因,在增强中没有树容器。有很多方法可以实现这样一个容器,并没有很好的方法来满足每个使用它的人。

需要考虑的一些问题:
  - 节点的子节点数是固定的还是可变的?
  - 每个节点有多少开销? - 即,你需要父指针,兄弟指针等   - 提供什么算法? - 不同的迭代器,搜索算法等。

最后,问题最终是一个对每个人都足够有用的树容器,对于大多数使用它的人来说太重了。如果您正在寻找功能强大的东西,Boost Graph Library本质上是树库可用的超集。

以下是一些其他通用树实现:
  - Kasper Peeters' tree.hh
  - Adobe's forest
  - core::tree

答案 2 :(得分:50)

STL的理念是您选择基于保证的容器,而不是基于容器的实现方式。例如,您选择的容器可能基于快速查找的需要。对于你所关心的一切,容器可以实现为单向列表 - 只要搜索速度非常快,你就会感到高兴。那是因为你无论如何都没有触及内部,你正在使用迭代器或成员函数进行访问。您的代码不受容器实现方式的限制,而是绑定到它的速度,或者它是否具有固定和定义的顺序,或者它是否在空间上有效等等。

答案 3 :(得分:45)

  

“我想将对象的层次结构存储为树”

C ++ 11已经过去了,他们仍然认为不需要提供std::tree,尽管这个想法确实出现了(参见here)。也许他们没有添加这个的原因是,在现有容器之上构建自己的容易起来非常容易。例如......

template< typename T >
struct tree_node
   {
   T t;
   std::vector<tree_node> children;
   };

一个简单的遍历将使用递归...

template< typename T >
void tree_node<T>::walk_depth_first() const
   {
   cout<<t;
   for ( auto & n: children ) n.walk_depth_first();
   }

如果您想维护层次结构,您希望它与STL algorithms一起使用,那么事情可能会变得复杂。您可以构建自己的迭代器并实现一些兼容性,但是许多算法对层次结构没有任何意义(例如,任何改变范围顺序的东西)。即使在层次结构中定义范围也可能是一个混乱的业务。

答案 4 :(得分:42)

如果您正在寻找RB树实施,那么stl_tree.h也可能适合您。

答案 5 :(得分:12)

std :: map基于red black tree。您还可以使用其他containers来帮助您实现自己的树类型。

答案 6 :(得分:8)

在某种程度上,std :: map是一棵树(它需要具有与平衡二叉树相同的性能特征),但它不会暴露其他树功能。不包括真实树数据结构的可能原因可能只是不包括stl中的所有内容。可以将stl看作是用于实现自己的算法和数据结构的框架。

一般来说,如果你想要一个基本的库功能,那就不在stl中,修复就是看BOOST

否则,bunch libraries outthere,具体取决于树的需求。

答案 7 :(得分:6)

所有STL容器外部表示为具有一种迭代机制的“序列”。 树木不遵循这个习语。

答案 8 :(得分:4)

因为STL不是“一切”库。它基本上包含构建东西所需的最小结构。

答案 9 :(得分:4)

这个看起来很有前途,似乎是你正在寻找的: http://tree.phi-sci.com/

答案 10 :(得分:2)

IMO,遗漏。但我认为有充分的理由不在STL中包含Tree结构。维护树有很多逻辑,最好将成员函数写入基础TreeNode对象。当TreeNode被包裹在STL标题中时,它会变得更加混乱。

例如:

template <typename T>
struct TreeNode
{
  T* DATA ; // data of type T to be stored at this TreeNode

  vector< TreeNode<T>* > children ;

  // insertion logic for if an insert is asked of me.
  // may append to children, or may pass off to one of the child nodes
  void insert( T* newData ) ;

} ;

template <typename T>
struct Tree
{
  TreeNode<T>* root;

  // TREE LEVEL functions
  void clear() { delete root ; root=0; }

  void insert( T* data ) { if(root)root->insert(data); } 
} ;

答案 11 :(得分:2)

我认为没有stl树有几个原因。主要树是递归数据结构的一种形式,它像容器(列表,向量,集)一样,具有非常不同的精细结构,这使得正确的选择变得棘手。使用STL,它们也很容易以基本形式构建。

可以将有限的有根树视为具有值或有效负载的容器,例如A类的实例,以及可能为空的根(子)树集合;没有子树的树虽然是叶子。

template<class A>
struct unordered_tree : std::set<unordered_tree>, A
{};

template<class A>
struct b_tree : std::vector<b_tree>, A
{};

template<class A>
struct planar_tree : std::list<planar_tree>, A
{};

人们必须考虑一下迭代器设计等,以及允许在树之间定义和有效的产品和联产品操作 - 并且原始的stl必须写得很好 - 以便空集,矢量或列表容器在默认情况下实际上没有任何有效负载。

树木在许多数学结构中起着至关重要的作用(参见Butcher,Grossman和Larsen的经典论文;还有Connes和Kriemer的论文,例如它们可以加入,以及它们如何被用来枚举)。认为他们的角色只是为了促进某些其他操作是不正确的。相反,由于它们作为数据结构的基本作用,它们促进了这些任务。

然而,除了树木,还有“共同树”;上面的树都有一个属性,如果你删除了根,你就删除了所有内容。

考虑树上的迭代器,可能它们将被实现为一个简单的迭代器堆栈,一个节点,它的父节点,......直到根节点。

template<class TREE>
struct node_iterator : std::stack<TREE::iterator>{
operator*() {return *back();}
...};

然而,你可以拥有任意多的人;它们共同构成了一个“树”,但是所有箭头都朝着根方向流动,这个共同树可以通过迭代器迭代到平凡的迭代器和根;但是它不能跨越或向下导航(其他迭代器不为它所知),也不能删除迭代器的集合,除非跟踪所有实例。

树木非常有用,它们有很多结构,这使得获得明确正确的方法成为一个严峻的挑战。在我看来,这就是为什么它们没有在STL中实现的原因。此外,在过去,我看到人们变得宗教,并且发现包含自己类型的实例的容器类型的想法具有挑战性 - 但是他们必须面对它 - 这就是树类型所代表的 - 它是一个包含可能是(较小的)树木的空集合。当前语言允许它没有挑战,提供container<B>的默认构造函数不会为B等在堆上(或其他任何地方)分配空间。

如果这样做,我会很高兴以良好的形式进入标准。

答案 12 :(得分:0)

通读这里的答案,常见的命名原因是:一个人不能遍历该树,或者该树不具有与其他STL容器类似的接口,并且不能使用具有这种树结构的STL算法。

考虑到这一点,我尝试设计自己的树数据结构,该结构将提供类似STL的接口,并尽可能地与现有STL算法一起使用。

我的想法是,树必须基于现有的STL容器,并且不能隐藏该容器,以便可以与STL算法一起使用。

树必须提供的另一个重要功能是遍历迭代器。

这就是我想出的:https://github.com/cppfw/utki/blob/master/src/utki/tree.hpp

以下是测试:https://github.com/cppfw/utki/blob/master/tests/tree/tests.cpp

答案 13 :(得分:-8)

所有STL容器都可以与迭代器一起使用。你不能让迭代器成为一棵树,因为你没有“一个正确”的方式来穿过这棵树。